# 学习内容
SpringCloud Hoxton.SR8 耐菲
RabbitMQ
分布式事务
RocketMQ
Docker
Elasticswarch
Kubernetes
Lucene Solr
# 一. idea
plugin EditStarters, Free Mybatis Plugin, Maven Helper
中央仓库
## STS
# 二. SpirngCloud
## A. 本阶段内容
SpirngCloud Hoxton.SR8 微服务
Eureka 注册中心
Feign 远程调用
Feign + Ribbon 负载均衡
Zuul 网关
Zuul + Ribbon 负载均衡
Zuul + Hystrix 容错和限流
HystrixDashboard + actuator 链路监控
Turbine 聚合日志
Spring cloud config 配置中心
## 服务
ItemServiceApplication
UserServiceApplication
OrderServiceApplication Feign
EurekaApplciation http://eureka1:2001/
ZuulApplication http://localhost:3001/item-service/item/23/?token=123
HystrixDashboardApplication http://localhost:4001/hystrix -> http://localhost:3001/actuator/hystrix.stream
TurbineApplication http://localhost:5001/turbine.stream聚合了3001和3002 http://localhost:4001/hystrix -> http://localhost:5001/turbine.stream
SpringCloudConfigApplication http://localhost:6001/item-service/dev
## 0. 集成多种工具,解决微服务中的各种问题
- 注册和发现
- Nacos
- Eureka
- 远程调用 - OpenFeign
- 负载均衡
- 重试
- 系统容错和限流 - Sentinel, Hystrix
- 降级
- 熔断
- 错误监控 - Hystrix Dashboard、Turbine
- 配置中心 - Nacos、Config
- 链路跟踪 - Sleuth、Zipkin
## 1. 依赖 pom.xml
> 打包方式
<packaging>pom</packaging>
> jackson
json互转工具
> lombok
> javax.servlet-api
创建cookie
> slf4j-api
> commons-lang3
apache的工具包
> maven-compiler-plugin
maven插件
## 2. 项目结构
springcloud1
sp01-commons
pojo
service(Interface)
web.util
sp02-itemservice
service(impl)
controller
sp03-userservice
service(impl)
controller
sp04-orderservice
service(impl)
controller
## 3. eureka 注册中心 --服务注册,服务发现,维护注册信息
> nacos, eureka, consul, etcd, zookeeper
>*1* 注册中心业务:
把服务注册在里面, 服务注册 服务发现
服务A 调用 服务B, 先从注册中心获取注册表得到所有, 找到对应的服务地址. A 在注册中心得到 B 的服务地址 再调用B的业务
>*2* eureka:
作为springboot服务
a. 禁用自我保护模式 --在集群中 ---eureka:server:enable-self-preservation: false
b. 主机名 -- ---instance:hostname: eureka1
c. 不向自己注册, 不从自己拉取 --集群中多个eureka要互相注册拉取 ---client:register-with-eureka: false fetch-registry: false
>*3* eureka服务器搭建:
1. 新建服务 sp05-eureka
2. pom.xml 依赖: eureka server
3. application.yml
eureka:
server:
enable-self-preservation: false
instance:
hostname: eureka1
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka1:2001/eureka/
# 集群节点服务器默认http://localhost:8761/eureka/
a. 集群服务器之间, 通过hostname 区分不同的eureka
b. eureka.server.enable-self-preservation
eureka的自我保护状态,心跳失败的比例-15分钟内达到85%进入保护状态, 把实例注册信息保护起来, 不再注销任何微服务
c. eureka.client.register-with-eureka=false
不向自身注册
d. eureka.client.fetch-registry=false
不从自身拉取注册信息
e. eureka.instance.lease-expiration-duration-in-seconds
最后一次心跳后,间隔多久认定微服务不可用,默认90
4. 启动类注解:
@EnableEurekaServer --触发eureka服务器的自动配置
### hosts: C:\Windows\System32\drivers\etc
>*4* eureka客户端搭建
---在需要在注册中心注册的服务中,配置
- 02-item 03-user 04-order 配置
1. pom.xml
eureka client依赖
2. application.yml
配置eureka server地址
eureka:
client:
service-url:
defaultZone: http://localhost:2001/eureka
eureka.instance.lease-renewal-interval-in-seconds
心跳间隔时间,默认 30 秒
defaultZone,默认位置,可以修改为具体地理位置,比如:beiJing, shangHai, shenZhen 等,表示 eureka 服务器的部署位置, 需要云服务器提供
eureka.client.registry-fetch-interval-seconds
拉取注册信息间隔时间,默认 30 秒
3. 启动类注解
@EnableDiscoveryClient
>*5* eureka运行机制
1. 注册: 客户端 反复连接服务器, 直到注册成功为止
2. 拉取: 客户端 每30秒拉取注册表, 刷新注册表
3. 心跳: 客户端 30秒发送一次, 服务端连续3次收不到-删除这个服务,
4. 自我保护模式: 15分钟内,85%服务器出现心跳异常(又一次未收到), 保护所有注册信息不删除, 服务端连续3次收不到心跳-不删除
>*6* 高可用
1. item-service 高可用
启动两个服务(不同端口) 8001 8002
a. 打包package
java -jar item.jar 使用的端口是yml中的8001
java -jar item.jar --server.port=8002 使用的是设置的8002
b.idea启动配置
edit configuration
-> program arguments -> --server.port=8001
Sp02ItemServiceApplication-8002 -> copy configuration -> 8002
2. eureka高可用
a. profile配置 profile名字就是eureka1
application-eureka1.yml
eureka:
instance:
hostname: eureka1
client:
register-with-eureka: true #profile的配置会覆盖公用配置
fetch-registry: true #profile的配置会覆盖公用配置
service-url:
defaultZone: http://eureka2:2002/eureka #eureka1启动时向eureka2注册
b. 使用配置
打包运行: java -jar eureka.jar --spring.profiles.active=eureka1
idea启动配置: program arguments
## 4. Feign --远程调用
订单服务
获取订单
获取用户
获取订单列表
保存订单
增加用户积分
减少商品库存
>*1* 订单服务
a. 依赖: openfeign
b. 启动类注解: @EnableFeignClients
c. 定义声明式客户端接口(远程调用服务接口)
ItemClient
UserClient
1. 调用哪个服务 @FeignClient(name = "item-service")
2. 调用哪个路径 @GetMapping("/{orderId}")
3. 提交什么参数 JsonResult<List<Item>> getItems(@PathVariable String orderId);
d. service使用feign来远程调用
注入接口
@Autowired
private ItemClient itemClient;
图标请求
@GetMapping("/favicon.ico")
public void ico(){}
## 5. Feign 集成 ribbon
>*1* 默认实现负载均衡
feign底层已经实现了,
>*2* 默认实现重试
调用远程服务时, 远程服务出错, 可以重试或者负载均衡
a. 重试参数
ribbon.MaxAutoRetries=0 --单台服务器的重试次数
ribbon.MaxAutoRetriesNextServer=1 --更换服务器次数
ribbon.ReadTimeout=1000 --发送请求后, 等待响应的超时时间
ribbon.ConnectTimeout=1000 --与后台服务器建立网络连接的超时时间
ribbon.OkToRetryOnAllOperations=false --是否对所有类型请求都进行重试,默认只对get请求重试
b. 测试
延迟 ItemController
## 6. Zuul API网关
a. zuul所有的功能都是通过过滤器实现的,
前置过滤器 pre-filtes
路由 routing
后置 post
错误 error
b. zuul的配置
自动配置, 会从Spring容器发现这个过滤器实例, 自动完成配置
>*0* 新模块 sp06-zuul
a. 依赖: eureka-client, zuul, sp01
b. .yml配置
路由-routes --路径和后台服务之间的映射
zuul: # 默认配置-根据注册表设置
routes:
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
c. 启动类注解
@EnableZuulProxy
>*1* 功能
统一的入口 --转发
统一的权限校验
集成ribbon
集成hystrix
>*2* 统一的权限校验 过滤器
过滤器, 在过滤器中检查用户权限
http://localhost:3001/item-service/item/fds123?token=fdjkslf13fd1s3
a. 继承Zuul的过滤器 ZuulFilter, 实现方法
filtertype() 过滤器类型,
FilterConstants.PRE_TYPE
filterOrder() 过滤器顺序号,
前5个是默认的过滤器, 加在第6个
在第5个过滤器中, 向上下文对象添加了serviceId属性, 后面的过滤器才能访问serviceId属性
shouleFilter() 针对请求是否执行过滤代码,
实现: 判断调用的后台服务, 服务id
获取服务id RequestContext.getCurrentContext().get("FilterConstants.SERVICE_ID_KEY");
run() 自定义过滤器具体代码实现
请求可以执行 run(), 再在这里判断是否有权限
没有权限 阻止继续调用requestContext.setSendZuulResponse(false);
b. 注解 @Component
>*3* 网关超时 GateWay Timeout 504
## 7. zuul集成ribbon
默认实现了负载均衡, 没有启动ribbon重试
尽量步骤网关添加重试, 否则可能造成大面积服务器压力倍增
启动重试(Zuul网关不推荐)
a. 添加 spring-retry依赖
b. yml配置 zuul.retryable=true
c. 有默认重试参数, 可以修改配置
对所有服务通用
ribbon:
MaxAutoRetries: 1 --单台服务器的重试次数
MaxAutoRetriesNextServer: 1 --更换服务器次数
只对item服务有用
item-service:
ribbon:
MaxAutoRetries: 0
## 8. zuul集成hystrix
容错和限流 链路监控 网关出错--会调用hystrix
a. 容错:
降级: 当调用后天服务出错, 执行当前服务中一段 降级 代码, 返回降级结果
b. 限流:
熔断: 调用后台服务流量过大时, 后台服务出现故障, 可以断开链路, 减轻后天服务压力
c. 降级:
调用后台服务出错: 500, 后台服务不存在, 调用超时, 默认1秒
实现:
依赖: 间接依赖
配置: 无
代码: 实现 FallbackProvider 接口 @Component
getRoute()
设置针对哪个后台服务进行降级
*/null 对所有服务应用降级类
item-service 只针对item服务应用降级类
fallbackResponse()
向客户端发回的降级响应
- 错误提示
- 缓存数据
new ClientHttpResponse(){}
zuul默认已经启动hystrix, 自动从容器中读取到实现了接口的实例,
d. Hysrix超时
默认1秒超时
ribbon重试, Hysrix超时会自动配置成ribbon的最大超时时间
设置超时时间:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
e. 熔断:
后台服务流量过大, 出现故障,
发生条件: 10秒20次请求(必须首先满足), 50%失败执行降级(),
断路器打开后: 一段时间后,进入半开状态, 半开状态下,会尝试向后天服务发送一次客户端调用
调用成功:
f. zuul 降级
https://gitee.com/zhaoqing_lj/msapro2104/tree/master/springcloud1/sp06-zuul/src/main/java/cn/tedu/sp06/fallbackp
g. 请求 报500
http://localhost:3001/order-service/order/1
zuul -> order -> { user, item }
OrderFallback.java ---降级代码
code: 500,msg: "后台服务出错, 请稍后重试" ------ item服务有延迟, zuul超时-降级了
## 9. Actuator
SpringBoot提供的一个项目监控工具, 提供项目的多种监控日志
a. 监控日志:
健康状态;
spring容器中所有的对象;
springmvc设置的所有路径;
环境参数, 环境变量;
虚拟机的堆内存;
b. 实现:
依赖: 间接依赖, zuul中有,
yml: 暴露哪些监控日志
m.e.w.e.i=* 暴露全部监控数据
m.e.w.e.i=health,beans,mapping,hystrix.stream
management:
endpoints:
web:
exposure:
include:
- health
- beans
c. 访问路径 --暴露的监控日志
http://localhost:3001/actuator
### java虚拟机内存分析
MAT
## 10. hystrix dashboard 链路监控
Hystrix仪表盘: 监控Hystrix降级和熔断情况
把一个服务的监控日志(actuator)以图表的形式展示
a. Hystrix利用Actuator工具,暴露自己的监控日志,
b. 搭建仪表盘:
创建module: sp07-hystrix-dashboard
依赖: hystrix-dashboard
yml配置:
允许抓取的服务列表
启动类注解:
@EnableHystrixDashboard
访问路径:
localhost:4001/hystrix
把路径放在 Hystrix Dashboard http://localhost:3001/actuator/hystrix.stream
可以是完全独立的项目, 不是在注册中心获取的服务
## 11. 压力测试工具
httpd-2.4.39-o102r-x86-vc14/Apache24/bin
用 ab 工具,以并发50次,来发送20000个请求
ab -n 20000 -c 50 http://localhost:3001/item-service/item/23/?token=123
ab -n 20000 -c 100 http://localhost:3001/user-service/user/23
## 12. Turbine
hystrix 日志的聚合工具
可以聚合多台服务器的Hystrix日志, Hystrix Dashboard 可以从 turbine 拉取 聚合之后的日志,
同时监控多台服务器
实现:
module: sp08-turbine
依赖: eureka client, turbine
yml:
聚合的服务: zuul localhost:3001 localhost:3002 从注册表获取
给聚合的数据命名: default
new String("default")
turbine:
app-config: zuul
cluster-name-expression: new String("default")
启动类:
@EnableTurbine
访问:
http://localhost:5001/turbine.stream
## 13. 部署分布式服务 (多台主机)
A. eureka 一台主机 172.18.6.73:2001
B. zuul 一台主机 172.18.6.74:3001
C. dashboard,turbine 一台主机 172.18.6.75:4001/hystrix 172.18.6.75:5001/turbine.stream
D. item 业务模块 所有主机
E. hosts
172.18.6.73 eureka1
172.18.6.73 eureka2
F.访问
http://172.18.6.73:2001 注册表
http://172.18.6.74:3001/item-server/item/23?token=sdf 访问item服务
http://172.18.6.75:4001/hystrix --填写--> http://172.18.6.75:5001/turbine.stream 仪表盘
## 14. vmware
> net 虚拟网络
vmnet8 -> 子网ip 192.168.64.0
> centos-8-2105
阿里yum安装源/扩展源, python, pip, ansible, ip-static(配置固定ip)/ip-dhcp(自动获取ip){脚本文件-方便配置ip地址}
已复制虚拟机
登录: root root
## 15. spring cloud config 配置中心
spring cloud config 放到 git仓库中/文件/数据库, 使用时拉取配置
git仓库 --github,gitee,gitlab,csdn的chinacode
a.配置文件:
文件夹: config
文件: 服务2,3,4的application
文件名: item-service-dev.yml user-service-dev.yml order-service-dev.yml
b.git仓库:
https://gitee.com/zhaoqing_lj/msapro2104.git
配置用户名/密码:
qingjing20140614@163.com 997z185q123f1fgfd8fdsf1fds5
路径:
msapro2104/springcloud1/config
端口:
java -jar i.jar --server.port=8002
.yml
spring.cloud.config.override-none: true 远程下载配置 不会 覆盖本地配置
c.配置中心
仓库的地址
存放配置文件的文件夹
D. 搭建配置中心
module: sp09-config
依赖: eureka client, config server
yml:
git仓库地址:
存放配置文件文件夹:
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/zhaoqing_lj/msapro2104.git
search-paths: springcloud1/config
username: qingjing20140614@163.com
password: zq1815
启动类注解:
@EnableConfigServer
访问:
http://localhost:6001/item-service-dev.yml
http://localhost:6001/item-service/dev
服务注册到eureka, 目的服务配置需要放到git的服务 要从eureka中获取配置中心服务, 再从配置中心服务中获取远程配置 到自己的服务
E. 2,3,4 服务 从配置中心获取 配置信息
依赖: spring-cloud-starter-config
yml: bootstrap.yml
. 连接eureka
. 指定配置中心 服务id
. 指定下载的配置文件
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka
spring:
cloud:
config:
discovery:
enabled: true
service-id: config-server
name: user-service
profile: dev
### 16. MQ 自动刷新配置
nacos, 更改配置后, 后端服务自动刷新配置
springcloudconfig, 需要MQ, 通过消息给服务 刷新配置
更改配置后, 配置中心发送消息要求服务器更新配置信息, 配置中心把消息发送给MQ, MQ再把消息给各个服务 服务刷新配置
RabbitMQ 添加到 springcloud中
A.Bus 辅助完成配置刷新指令的收发操作
B.业务服务, 配置中心添加 Bus 组件 RabbitMQ-api
a. 依赖: Bus, Binder-rabbit, spring-boot-starter-amqp, spring-rabbit-test
b. yml:
配合rabbitmq连接信息
spring:
rabbitmq:
host: 192.168.64.140
port: 5672
username: admin
password: admin
C.配置中心暴露刷新 端点
a. 依赖: actuator(已有)
b. yml:
暴露刷新端点
management:
endpoints:
web:
exposure:
include: bus-refresh
D.访问Bus
刷新所有服务
post ---> http://localhost:6001/actuator/bus-refresh ---> Bus发送消息给RabbitMQ, 刷新配置, 业务服务重新连接了配置中心拉取了配置
只刷新item-service
post ---> http://localhost:6001/actuator/bus-refresh/item-service
E.修改配置, 服务自动刷新配置
使用User服务, 添加用户注入到userJson
a. 注解: @RefreshScope // 需要注入(@Value()) 的业务层 添加注解 // 配置刷新到新的用户配置, 可以重新注入到对象中
b. 使用Bus( post ---> http://localhost:6001/actuator/bus-refresh/user-item ) 刷新业务配置-使业务重新在配置中心拉取配置
刷新配置, 从配置中心拉取了配置, (不是重启了服务)
### 17. Bus 工具/组件 消息总线
发送消息
组件
# 三. RabbitMQ
传递消息
## 1. 消息服务器 / 消息队列 Message Queue / 消息中间件 Broker
## 2. 市场上的消息服务器
RabbitMQ,
RocketMQ(阿里事务消息),
TubeMQ(腾讯万亿级别),
Kafka,
ActiveMQ,
## 3. Docker环境
A. 克隆centos-8-2105 docker-base
B. 设置ip
./ip-dhcp ifconfig
C. Mobaxterm连接服务器
D. Docker在线/离线安装
E. 安装Docker
F. 克隆docker-base rabbitmq
设置ip ./ip-static ip:192.168.64.140
## 4. 搭建RabbitMQ服务器 docker运行
A. RabbitMQ镜像
在线 docker pull rabbitmq:management
离线 docker load -i rabbit-image.gz
B. docker
防火墙:
systemctl stop firewalld / disable
重启docker:
systemctl restart docker
C. 从镜像启动RibbitMQ --在线拉取的新版本 3.9 以上
配置文件:
mkdir /etc/rabbitmq
vim /etc/rabbitmq/rabbitmq.conf
default_user = admin
default_pass = admin
运行:
docker run -d --name rabbit -p 5672:5672 -p 15672:15672 \
-v /root/msapro/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-e RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf rabbitmq:management
## 5. RabbitMQ 六种模式
简单模式
工作模式
发布订阅模式
路由模式
主题模式
RPC模式
## 6. RabbitMQ使用场景
A.服务与服务之间 解耦
服务之间互相调用 直接调用 耦合高
服务之间 插入rabbitmq服务 间接调用 降低耦合
服务A -> rabbitmq服务 {服务B, 服务C, 服务D, 服务E} 增加减少只需要从rabbitmq获取消息就行
B.流量削锋
大量的请求 -> rabbitmq服务器 -> 数据库
rabbitmq 暂时缓存数据 原来: 并发的请求数据库 rabbitmq: 没有并发,一条一条处理
C.异步调用
服务器A(a,b,c,d) 请求 服务器B
A把a 发给rabbitmq rabbitmq给B, A可以继续执行b,c..下面的业务
## 7. 搭建maven项目
A.依赖: amqp-client(com.rabbitmq)
B.消息
消息发送成功 channel.basicPublish()
http://192.168.64.140:15672/ 获取到消息队列中的消息
消息接收成功 channel.basicConsume()
http://192.168.64.140:15672/ 消息队列中 就是空了
取走了消息就没有了,
C.模式
https://gitee.com/zhaoqing_lj/msapro2104/tree/master/rabbitmq/rabbitmq-api/src/main/java/rabbitmq
简单模式 : m1
1.连接 ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.64.140"); factory.newConnection() connection.createChannel();
2.创建 helloWorld 队列 channel.queueDeclare("helloWorld",false,false,false,null);
3.向 helloWorld 队列发生消息 channel.basicPublish("","helloWorld",null,in.getBytes());
工作模式 : m2
多个消费者, 轮循接收, 负载均衡提高效率
发布订阅模式/群发模式 : m3
交换机 fanout
发送给所有 consumer 每个consumer收到所有消息, 使用交换机,fanout 扇形发布
rabbitmq 交换机 ( Direct Fanout Topic )
路由模式 : m4
交换机 direct,
绑定键, 给拥有绑定键的队列发送消息,
路由关键词:匹配
producer -routingKeyOne-> exchanges
consumer -routingKeyTwo-> exchanges
发送消息+routingKeyOne 当routingKeyTwo=routingKeyOne时的consumer才能接收到消息
主题模式 : m5
交换机 Topic
关键词 (和路由一样)
关键词有特殊规则, aa.bb.cc.dd *.*.cc.dd aa.#
D.消息合理分发
a. 手动 Ack -> 手动回执
接收消息 参数 第二个 false
channel.basicConsume(Common.name,false,deliverCallback,cancelCallback);
Ack中手动回执 回执位置 message{ envelope{ tag-int } }
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
b. 手动 qos (只在手动Ack时有效) 预抓取
channel.basicQos(1); --接收消息前 --每次接收一条,处理完之前不接受下一条
E.回滚消息
当接收消息服务 Consumer 宕机/停机 时, 消息会回滚到RabbitMQ服务器 内存
F.消息持久化
队列持久化,
channel.queueDeclare(name,true,false,false,null); 第二个参数 持久队列
消息持久化
发送消息, 添加持久参数
channel.basicPublish("",Common.name, MessageProperties.PERSISTENT_TEXT_PLAIN,s.getBytes());
G.方法
new ConnectionFactory(); --RabbitMQ的工厂连接 (com.rabbitmq.client.ConnectionFactory;)
factory.newConnection(); --从工厂获取一个连接
connection.createChannel(); --创建通信的通道
创建队列
channel.queueDeclare(queueName,durable,exclusive,autoDelete,arguments) --队列声明
创建交换机
channel.exchangeDeclare(exchangesName, BuiltinExchangeType.FANOUT-交换机类型); --交换机声明
向队列发消息
channel.basicPublish(exchange,routingkey,props,body-消息内容); --基础发布
接收消息
channel.basicConsume(queueName,autoAck,deliverCallback-处理消息,cancelCallback-取消消息) --基础消费
手动qos
channel.basicQos(1); --预抓取,每次接收一条,处理完之前不接受下一条
绑定交换机
channel.queueBind(queueName,exchangesName,routingKey); --队列捆绑
## 8. RabbitMQ使用案例
A.Bus配置刷新指令
主题模式
解耦,异步调用
B.sleuth+zipkin联络跟踪
简单模式
解耦,流量削峰
C.购物系统 产生订单 到数据库
订单的流量削峰
购物系统生成订单发送到 rabbitmq
后台消费者 一个一个 顺序处理订单存储
D.
# 四. sleuth
在各个模块中产生链路跟踪日志
A -> B -> C -> D
A, 4F5D6S7RE3SF4, 4F5D6S7RE3SF4, TRUE
请求服务时产生id(A-id),
A-id作为整条链路的id(说明是同一条链路,一次调用过程)
TRUE(true把日志发送到zipkin,抽样10%发到zipkin)
## 1. 添加sleuth
依赖: spring-cloud-starter-sleuth
配置: 自动配置
## 2. 请求
A.报500
zuul 06 -> order 03 -> { user 04, item 02 }
code: 500,msg: "后台服务出错, 请稍后重试" item服务有延迟, zuul超时-降级了
B.后台输出日志 sleuth
INFO [zuul,c2013aea7e6c21aa,c2013aea7e6c21aa,true]
# 五. sleuth + zipkin
链路跟踪
## 1. sleuth 产生的日志 发送给 zipkin
A.直接连接zipkin, 提交数据
紧耦合
B.通过消息服务,发送日志
a.解耦
b.流量削峰
## 2. 添加zipkin 客户端
A.通过消息服务发送日志 需要RabbitMQ
B.依赖:
zipkin客户端--spring-cloud-starter-zipkin,
rabbitMQ--spring-boot-starter-amqp
C.yml:
日志发送方式
rabbitmq连接配置
spring:
rabbitmq:
host: 192.168.64.140
port: 5672
username: admin
password: admin
zipkin:
sender:
type: rabbit
## 3. zipkin服务
A.下载服务
https://github.com/openzipkin/zipkin Quick-start -> latest released server
B.启动服务
java -jar zipkin-server-2.23.2-exec.jar --zipkin.collector.rabbitmq.uri=amqp://admin:admin@192.168.64.140:5672
C.访问
http://localhost:9411/zipkin
# 六. 拼多商城 购物系统 RabbitMQ
购物系统产生大量订单 存储 到数据库
使用 RabbitMQ 做流量削峰, 在rabbitmq中排队处理(缓存在rabbitmq中)
## 1. 导入项目 pd-web 提交订单 saveOrder
pom.xml -> make as maven
jdk1.8
## 2. 导入数据库
删除数据 pd_user, pd_order, pd_order_item
## 3. 启动项目
配置项目的启动配置
项目配置(configuration) ->
working directory 设置到 pd-web 模块目录,然后重启项目
或者 program arguments -> $%MODULE_WORKING_DIR%$
## 4. 修改订单, 向 rabbitmq 发送订单数据 ------------------------------
A.依赖: rabbitmq---spring-boot-starter-amqp
B.yml: 连接rabbitmq
C.创建 队列orderQueue 参数的封装对象,
在 启动类 创建 orderQueue,true,false,false
org.springframework.amqp.core.Queue 包 封装队列
自动配置类-RabbitAutoConfiguration 使用参数创建队列
D.在OrderServiceImpl 注入spring提供的rabbintmq工具: AmqpTemplate
E.amqpTemplate.convertAndSend() 发送订单
转换并发送方法, order对象会自动转换成byte[]数组再 发送 --序列化
F. 发送订单 到 购物车 支付-保存订单 到了 RabbitMQ
## 5. 项目 pd-web-consumer 消费订单 ------------------------------------
接收消息, 把订单存储到数据裤
类 OrderConsumer
A. 注解: @RabbitListener(queue="orderQueue")通过注解配置, 接收消息
@Component
B. 处理消息的方法, @RabbitHandler, 具有这个注解的方法接收消息
C. 调用业务方法完成订单存储
Z. 总结
接收消息 注解@RabbitListener(queues="orderQueue") queues指定队列
接收队列消息, 反序列化 成一个订单实例给方法参数
@RabbitHandler 配合@RabbitListener注解, 指定处理消息的方法
## 6. 订单 生成-用户支付 消费-订单保存到数据库 ------------------------------
-----由把订单保存到数据库 改变为 先把订单发消息给rabbitmq,然后消费者接收消息 ---------
A.生成订单
(1)商品添加到购物车
(2)添加收货地址--原因:如果没有地址后端会判断报空指针异常
(3)用户支付订单-保存订单到RabbitMQ
B.保存订单
(1)consumer 从RabbitMQ中获取到队列中的消息
(2)执行service业务, 保存到数据库
# 七. rabbitmq 整合 springboot
依赖: spring-boot-starter-amqp, spring-rabbit-test
yml: spring.rabbitmq.host port username password
## 1. 模式
简单模式 : m1
发送: amqpTemplate.convertAndSend("helloworld","Hello World ZhaoQing"); // 自动转换byte数组再转换
接收: @RabbitListener(queues = "helloworld") public void receive(String msg){ // 参数接收到注解传过来的参数 System.out.println("收到 : "+msg); }
main: 配置queue参数: @Bean return new Queue("helloworld",false); 手动执行发送消息: 执行方法
工作模式 : m2
多个消费者, 轮循接收, 负载均衡提高效率
合理分发: spring默认ack自动回执, qos=1 yml prefetch=1
持久化: 队列 new Queue("task_queue",true); 消息 spring默认持久
发布订阅模式/群发模式 : m3
交换机 fanout
producer: amqpTemplate.convertAndSend("logs","",info);
consumer: @RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(name = "logs",declare = "false")))
main: 创建交换机, 执行send
路由模式 : m4
交换机 direct,
绑定键, 给拥有绑定键的队列发送消息, : key
主题模式 : m5
交换机 Topic
关键词 (和路由一样)
关键词有特殊规则, aa.bb.cc.dd *.*.cc.dd aa.#
# 八. 数据库初始化工具/模块
重置所有数据库表
SpringBoot db-init
## 1. 新建
依赖: jdbc api, mysql driver
yml: 数据源datasource
resource:
sql/ 1.sql,2.sql,3.sql,..
启动来: 执行sql脚本-初始化数据库
## 2. jdbc 脚本执行器
A.Spring 提供的 ScriptUtils.executeSqlScript()
参数: 数据库的链接 java.sql.Connection , 文件资源对象
B. 文件资源对象
路径资源: new ClassPathResource(文件路径String, DatabaseInitApplication.class.getClassLoader());
编码转换: new EncodedResource(classPathResource, "utf-8");
# 九. 分布式事务 部署服务 案例 (无事务处理) seata-at
## 1. 数据库初始化
## 2. eureka 注册中心
搭建 8761 模块 eureka
依赖: eureka server
yml: 禁用保护模式, 单台服务器(不注册,不拉去),
启动类注解: @EnableEurekaServer
## 3. 父子项目
父: order-parentTwo
pom.xml
子: account 实现扣减账户金额, storage 减少库存, order 保存订单 调用s和c
依赖: 清空(都在父项目里)
yml:
app.name, port, eureka, datasource, mybatisp, logging
pojo, Dao, Service, Controller
## 4. 全局唯一id发号器
https://github.com/lookingatstarts/easyIdGenerator ,下载发号器项目
依赖: eureka
yml: eureka
配置详情:
两个算法
easy-id-generator.snowflake
easy-id-generator.segment 数据库方式
db-list: seata_order 数据库列表, 使用配置文件seata_order.properties
访问: http://localhost:9090/segment/ids/next_id?businessType=order_business
## 5. Feign 远程调用
order订单添加Feign,调用库存和账户服务
a.调用发号器获得全局唯一id
b.调用库存服务减少商品库存
c.调用账户服务扣减用户金额
A.依赖:
spring-cloud-starter-openfeign
B.yml配置
超时时长: ribbon.ReadTimeout=10000
C.注解
@EnableFeignClients
D.声明式客户端接口
@FeignClient(name="easy-id-generator") 描述接口(指定被调用服务名)
@GetMapping("/segment/ids/next_id") 描述方法(执行被调用服务的方法)
@FeignClient(name = "EASY-ID-GENERATOR")
public interface EasyIdGeneratorClient {
@GetMapping("/segment/ids/next_id")
String nextId(@RequestParam String businessType);
}
# 十. 分布式事务
分布式事务: Seata AT, TCC, SAGA, XA, 可靠消息最终一致性(异步确保), 最大努力通知
A B C 分布式服务 A -> B A -> C
B 发生事务 C 知道不了
解决: 事务协调器
## 1.Seata分布式事务框架
提供高性能和简单易用的分布式事务服务
支持: AT, TCC, SAGA, XA
# 十一. Seata AT
## 1. Seata AT 基本原理
### 1.1 事务协调器 Transaction Coordinator
A. 第一阶段:
执行各分支事务 @Transactional
B. 第二阶段:
控制全局事务最终提交或回滚
### 1.2 第一阶段
#### 1.2.1 事务管理器 Transaction Manager
事务管理器 向 事务协调器 申请开启全局事务(产生XID) TM 向 TC 申请开启全局事务, TC 产生全局事务id(XID)
#### 1.2.2 资源管理器 Resource Manager
执行业务, 开启 资源管理器并把XID给它,
职责: 管理分支事务(本地事务), 与TC通信,上报分支事务的执行状态,接收全局事务的提交和回滚指令
RM 向 TC 注册分支事务, 把分支事务纳入对应的全局事务管理
分支事务执行成功, RM 发送事务状态给 TC, TC 将事务状态给TM
### 1.3 第二阶段 提交
全部分支事务执行完毕, TM 收集了所有分支事务的 事务状态 -> 做做种决策, 让RM 发送提交指令完成提交操作
### 1.4 事务协调器 第一阶段/第二阶段 执行流程总结
TC 事务协调器 全局事务,分支事务
TM 事务管理器 收集分支事务的事务状态, 决策全局事务提交/回滚
RM 资源管理器 管理分支事务, 发送分支事务状态, 全局事务的提交/回滚指令/操作
#### 1.4.1 第一阶段
a. TM 向 TC 申请开启全局事务, TC 产生 XID 并发给 TM
b. 执行事务, 启动 RM 并把 XID 给 RM, RM 携带 XID 向 TC 注册分支事务
c. 事务成功, RM 上报事务状态给 TC, TC 把事务状态发送给 TM,
d. 执行其他事务, 注册分支事务, 最终 TM 手机所以分支事务的事务状态
#### 1.4.2 第二阶段
a. TM 收集到所有分支事务的事务状态, 决策全局事务状态,
b. TM 把全局事务状态发送给 TC
c. TC 根据全局事务状态给 RM 发送指令, RM 执行 提交/回滚 指令/操作
## 2. 具体工作机制
### 2.1 第一阶段
库存表 库存 50 改为 40
a. 把表中原来的数据取出来
b. 进行修改操作
c. 取出表中的新数据
d. 把旧数据和新数据 合并, 存放到 事务回滚日志表
e. 第一阶段完成, 将状态上报给 TC
### 2.2 第二阶段
事务失败--回滚
a. TC 发送回滚指令 给 RM
b. 根据事务回滚日志表, 将库存表中的数据恢复
c. 删除事务回滚日志, 完成回滚操作
事务成功--提交
a. TC 发送提交指令 给 RM
b. 删除事务回滚日志, 完成第二阶段提交操作
## 3.TM TC RM
TC 全局协调 单独的服务
TM 全局管理/存储事务状态日志 调用服务的服务--添加
RM 分支事务 被调用服务--添加
A->B A->C
TC seata-server服务
TM 在A中添加
RM B,C中添加
# 十二. 微服务添加 Seata AT 分布式事务
## 1. Seata Server - TC 全局事务协调器
A. 下载 https://github.com/seata/seata/releases
B. 配置文件
conf/registry.conf
连接eureka注册中心
conf/file.conf
seata server 运行时记录日志的数据库
bin/seata-server.bat
降低虚拟机内存大小(默认2G)
C. 启动
双击 seata-server.bat
## 2. 业务中添加 Seata AT 事务
A.依赖:
seata
B.配置:
application.yml --事务组的组名
spring.cloud.alibaba.seata.tx-service-group=order_tx_group
registry.conf --注册中心地址 新建
type=eureka
file.conf --事务组使用哪个协调器 新建
vgroupMapping.order_tx_group(组名) = "seata-server"(协调器) 要使用的协调器
C.自动配置类
创建数据源代理对象
DSAutoConfiguration @Configuration
a. 注入数据源配置 yml中的datasource
@ConfigurationProperties(prefix = "spring.datasource")
b. 数据源 @Bean
new HikariDataSource();
hikari的url是jdbcUrl(修改yml添加jdbcUrl,datasource下jdbcUrl: ${spring.datasource.url})
c. 数据源代理 @Bean
new DataSourceProxy(dataSource);
d. 指定注入spring哪个数据源对象
@Primary 首选对象
数据源配置类
@Configuration
public class DSAutoConfiguration {
注入数据源配置 yml中的datasource
@ConfigurationProperties(prefix = "spring.datasource")
数据源
指定连接池 new HikariDataSource();
@Bean
public DataSource dataSource(){return new HikariDataSource();}
数据源代理
@Primary
@Bean
public DataSource dataSourceProxy(DataSource dataSource){return new DataSourceProxy(dataSource);}
}
e. 排除spring默认数据源自动配置类,使用自己的 DSAutoConfiguration
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
D.业务方法注解
@Transactional 控制本地事务
@GlobalTransactionsl 启动全局事务 seata提供
业务模块创建TM 向TC 申请开启全局事务, TC 向 TM 发送 XID
第一个模块中添加
a. 本地事务注解 @Transactional 全局事务注解 @GlobalTransactional
添加到 调用其他服务的服务 方法上
@Transactional
@GlobalTransactional
public void create(Order order) {} // 创建订单order 调用 account账户 和 storage库存
E.被调用服务添加 Seata AT
依赖 seata
配置文件 application.yml registry.conf file.conf
自动配置类
数据源代理 DSAutoConfiguration
启动类注解
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
业务方法注解
@Transactional 本地事务
# 十三. 分布式事务 TCC
one. 每个阶段的数据操作要自己进行编码, 事务框架无法自动处理
two. 效率高, 不对数据加全局锁, 允许多个事务同时操作数据
## 1. 基本原理
A.第一阶段
Try --预留资源, 冻结数据
例如: 先把要扣减的金额预留(冻结)出来 --要处理的数据我先拿出来放起来,执行完我再处理数据
B.第二阶段
Confirm --确认/提交, 使用上一阶段预留的数据, 完成业务数据处理
Cancel --取消/回滚, 对第一阶段预留的数据, 再回复为原始数据
# 十四. 微服务添加 Seata TCC 分布式事务
## 1.使用Seata server
## 2.添加TCC事务
A.依赖:
seata
B.配置文件
application.yml
registry.conf
file.conf
C.事务自己实现 不像at事务自动配置
D.Mapper添加新的数据库操作
Try --冻结数据 update
向表中插入数据状态不可见
Confirm --确认提交
Cancel --取消,回滚
E.按照seata定义的规则, 添加 TccAction接口
添加TCC三个操作的方法,调用mapper的三个数据库操作
F.业务方法中, 手动调用第一阶段方法
全局事务 @GlobalTransactional
D E F 步骤
自己写代码执行冻结, 配置冻结操作的代码, 业务调用冻结方法
G.第二阶段不需要手动执行
@GolbalTransactional 全局事务 --在调用服务的服务上添加 一次, 被调用服务不需要添加
二阶段就会自动执行
## 3.TCC 基本原理的实现(需要自己写代码实现)
A.对数据库的操作, 具体业务代码, OrderMapper.xml中sql
B.OrderTccAction接口, 两个阶段要实现的业务方法
prepare()
commit()
rollback()
C.接口实现类
prepare() 执行 创建订单操作create()
commit() 执行 更新订单状态操作 updateStatus()
rollback() 执行 删除订单操作 deleteById()
D.业务
执行prepare()方法 --TCC的Try阶段
E.问题 ???
第一阶段: 手动执行了 prepare()
第二阶段: commit() 和 rollback() 怎么调用的
解决: @GlobalTransactional create() 方法上
两个阶段业务注解
@TwoPhaseBusinessAction(name = "OrderTccAction",commitMethod = "commit",rollbackMethod = "rollback")
F.幂等性控制
防止重复提交/回滚
官方案例,赋值粘贴
## 4.幂等性控制
如果重复执行提交或回滚, 就执行一次,结果一样
理解: 防止重复提交回滚, 重复执行,不执行二阶段
解决: 使用标记, 二阶段判断有没有标记,没有标记就是执行完了
ResultHolder
# 十五. RocketMQ 消息高可用 安装
不使用 docker 运行, 直接在linux中运行
## 1. 搭建rocketmq服务
jdk, jdk环境变量
rocketmq文件, rocketmq环境变量, rocketmq减小服务(name-server,broker)内存, 启动
A.需要的资源:
jdk1.8, rocketmq-all-4.7.0-bin-release, rocketmq-console-ng-1.0.1
B.jdk1.8
tar -xf jdk-8u212-linux-x64.tar.gz -C /usr/local/ 解压
vim /etc/profile jdk 配置文件修改, 在/usr/local下
# 在文件末尾添加以下内容:
export JAVA_HOME=/usr/local/jdk1.8.0_212
export PATH=$JAVA_HOME/bin:$PATH
source /etc/profile 使环境变量生效
C.rocketmq 二进制文件
a.下载 wget https://mirror.bit.edu.cn/apache/rocketmq/4.7.0/rocketmq-all-4.7.0-bin-release.zip
b.解压缩
unzip rocketmq-all-4.7.0-bin-release.zip -d /usr/local/
# 修改一下文件夹名,改成 rocketmq 方便使用
mv /usr/local/rocketmq-all-4.7.0-bin-release /usr/local/rocketmq
c.配置环境变量 ROCKETMQ_HOME 和 PATH
cd /usr/lcoal
vim /etc/profile
# 在文件末尾添加以下内容:
export ROCKETMQ_HOME=/usr/local/rocketmq
export PATH=$ROCKETMQ_HOME/bin:$PATH
d.让环境变量立即生效
source /etc/profile
e.减小rocketqm使用的内存
cd /usr/local/rocketmq/
修改 name server 内存改为 256m
# 编辑 bin/runserver.sh
vim bin/runserver.sh
# 找到文件中下面这一行:
JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
# 将 -Xms4g -Xmx4g -Xmn2g 修改为 -Xms256m -Xmx256m -Xmn128m
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
修改 broker 内存改为 256m
# 编辑 bin/runbroker.sh
vim bin/runbroker.sh
# 找到文件中下面这一行:
JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g"
# 将 -Xms8g -Xmx8g -Xmn4g 修改为 -Xms256m -Xmx256m -Xmn128m
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m"
f.启动rocketmq
先启动 name server
# 进入 rocketmq 目录
cd /usr/local/rocketmq/
# 启动 name server
nohup sh bin/mqnamesrv &
# 查看运行日志, 看到"The Name Server boot success."表示启动成功
tail -f ~/logs/rocketmqlogs/namesrv.log
再启动 broker
# 启动 broker, 连接name server: localhost:9876
nohup sh bin/mqbroker -n localhost:9876 &
# 查看运行日志, 看到"The broker[......:10911] boot success."表示启动成功
tail -f ~/logs/rocketmqlogs/broker.log
g.关闭防火墙
rocketmq的通信会用到多个端口, 为了方便测试我们关闭防火墙
# 关闭防火墙
systemctl stop firewalld.service
# 禁止防火墙开机启动
systemctl disable firewalld.service
h.测试
# 通过环境变量, 告诉客户端程序name server的地址
export NAMESRV_ADDR=localhost:9876
/usr/local/rocketmq
# 启动生产者来测试发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
# 启动消费者来测试接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
i.rocketmq命令
关闭 broker
mqshutdown broker
关闭 nameserver
mqshutdown namesrv
j.收发消息出现超时问题
cd /usr/local/rocketmq/
vim conf/broker.conf
末尾添加
brokerIP1=192.168.64.141
关闭 broker 服务
mqshutdown broker
重新使用 broker.conf 配置启动 broker
nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &
D.进程查看是否运行
jps
## 2. rocketmq注册中心,消息服务
name server --rocketmq的注册中心
内存 4G
broker --消息服务,消息中间件
内存 8G
## 3.管理界面
在开源项目 rocketmq-externals 中提供了rocketmq 的管理界面: 地址为: https://github.com/apache/rocketmq-externals
github 在国内访问缓慢, 也可以使用码云的镜像项目, 地址为: https://gitee.com/mirrors/RocketMQ-Externals
A.克隆项目
cd /usr/local/rocketmq/
# 克隆 rocketmq-externals 项目
git clone https://gitee.com/mirrors/RocketMQ-Externals
B.maven打包
yum install -y maven
# 进入管理界面项目的文件夹
cd RocketMQ-Externals/rocketmq-console
# 执行maven 打包命令, 执行时间较长, 请耐心等待
mvn clean package -Dmaven.test.skip=true
C.运行启动
# 运行管理界面
nohup java -jar rocketmq-console-ng-1.0.1.jar --server.port=8080 --rocketmq.config.namesrvAddr=localhost:9876 &
D.访问
http://192.168.64.141:8080
# 十六. rocketmq
## yi. 双主双从 同步赋值集群方案
## er. 基本原理
Topic 集群服务器
A.生产者负载均衡
轮循
B.消费者负载均衡
AllocateMessageQueueAveragely 平均分配 11,22,33
AllocateMessageQueueAveragelyByCircle 环形分配 1,2,3,1,2,3
## san. rocketmq 原生 API
### 0.module
依赖: rocketmq-store, rocketmq-client
### 1.同步消息
A.主从复制: 生发消息, 向消复制, 消返回给生
B.生产者
* 创建生产者, 设置 name server ---new DefaultMQProducer("producer1")
* 连接服务器, 启动, (从注册表中的地址连接消费者) ---producer1.setNamesrvAddr("192.168.64.141:9876"); producer1.start();
* 消息封装 Message ---new Message("Topic1","Tag1",str.getBytes());
* Topic --相当于一级分类 Tag --相当于二级分类
* 发送消息 ---producer1.send(message);
* 返回结果 [topic=Topic1(目录), brokerName=localhost.localdomain, queueId=3(消息序列id)], queueOffset=0(消息下标)]
C.消费者
* 创建消费者 ---new DefaultMQPushConsumer("consumer1");
* 设置name server ---consumer1.setNamesrvAddr("192.168.64.141:9876");
* 从哪里订阅消息 ---consumer1.subscribe("Topic1","Tag1"); Tag1 或 * 或 Tag1 || Tag2 || Tag3
* 处理消息的监听器 ---consumer1.setMessageListener(new MessageListenerConcurrently(){}--启动多个线程, 并发的处理消息)
* new MessageListenerConcurrently() {public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
* for (MessageExt messageExt : list) {
* String s = new String(messageExt.getBody());
* System.out.println("收到"+s);
* }
* return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // 成功
* // return ConsumeConcurrentlyStatus.RECONSUME_LATER; // 稍后会收到
* }
* 启动消费者 ---consumer1.start();
D.计时消息--延时消息
判断消息时间
### 2.异步消息
主从复制: 生发消息, 向消复制,同时返回给自己,
生产者
消费者
### 3.单向消息
生产者
消费者
### 4.顺序消息
A.消费者接收消息的顺序
问题: 发送时,放到队列中(轮循放), 多个队列(存放位置),多个线程(处理的快慢) (rocketmq 一个队列由一个消费者接收消息) 消费者接收到的消息 顺序就乱了
解决: 把消息发送到一个队列 且 消费者由一个线程处理
B.生产者 ---发送消息到同一个 消息序列
* 设置队列选择器
* producer2.send(message, new MessageQueueSelector() {}, orderId); // 参数: message-给Selector->message, 队列选择器, 选择依据-给Selector->object
* new MessageQueueSelector() {public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
* // 参数: 服务器端队列列表, 正要发送的消息, 队列选择依据
* Long orderId = (Long) o; int index = (int) (orderId % list.size()); return list.get(index); }
C.消费者 ---单线程接收消息
* 设置监听器(单线程)
* consumer2.setMessageListener(new MessageListenerOrderly() {}) //MessageListenerOrderly()启动的是一个单线程
* new MessageListenerOrderly() {public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
* for (MessageExt messageExt : list) {System.out.println("收到"+new String(messageExt.getBody()));}
* return ConsumeOrderlyStatus.SUCCESS;}
### 5.延时消息
A.生产者发送消息之前, 进行延时, 一段时间后消费者才能收到消息
B.时间级别
this.messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
生产者 ---message.setDelayTimeLevel(3);
消费者
### 6.批量消息
生产者
消费者
### 7.消息过滤
Tag 过滤
对自定义属性过滤
生产者
消费者
### 8.事务消息 高可靠消息
A.异步调用, 两个模块之间实现异步调用
事务消息的原理
发送者: 1.发送本消息(不会给消费者) 2.执行本地事务 3.提交消息
消费者: 接收消息不允许失败, 失败会一直重试18次, 最后不行成为死消息-人工处理
发送者: 1.发送本消息(不会给消费者) 2.执行本地事务 3.提交消息
* 创建事务消息产生者, 设置nameserver
* ---new TransactionMQProducer("producer3");
*
* 设置事务消息监听器 --执行本地事务, 处理服务器的回查
* ---producer3.setTransactionListener(new TransactionListener() {})
* new TransactionListener() {
* @Override // 执行本地事务
* public LocalTransactionState executeLocalTransaction(Message message, Object o) {
* System.out.println("执行本地事务, 参数: "+message);
* if (Math.random()<0.5){System.out.println("本地事务执行成功"); return LocalTransactionState.COMMIT_MESSAGE;
* }else {System.out.println("本地事务执行失败");return LocalTransactionState.ROLLBACK_MESSAGE;}}
*
* @Override // 回查
* public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
* System.out.println("处理服务器网络中断");return LocalTransactionState.UNKNOW;}}
*
* 启动
* 发送事务消息, 会触发监听器来执行本地事务
* --- producer3.sendMessageInTransaction(message,"执行本地事务的参数数据");
*
消费者: 接收消息不允许失败, 失败会一直重试18次, 最后不行成为死消息-人工处理
System.out.println("处理消息成功");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
System.out.println("处理消息失败");
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
# 十七. spirng boot 订单案例 整合 rockermq
## 1. model
依赖: rocketmq
rocketmq-spring-boot-starter
yml: name server, 生产者组组名
rocketmq:
name-server: 192.168.64.141:9876
producer:
group: order-producer
数据库表: tx_table 记录事务执行的状态
业务: TxInfo, TxMapper --insert, select 记录事务执行状态,应对回查
message: 封装消息数据的 AccountMeaage对象
工具: 添加JsonUtil工具, 处理消息数据的格式转换
业务代码: 先执行发送消息, 再执行订单业务
create(Order order){
rocketMQTemplate.sendMessageInTransaction("order-topic", msg, order);
}
监听器: 执行本地事务(存储订单), 事务状态回查
#https://blog.youkuaiyun.com/weixin_38305440/article/details/108609574?spm=1001.2014.3001.5502
# 零. 知识点
## application.yml中的 profile
springboot项目中的一小段配置, 可选择是否使用
主配置: 提取配置共性
profile: 相同配置属性,不同属性值
> application.yml
server:
port: 8001
# 项目使用 prof1
# yml中 主配置和prof1配置合并使用
spring:
profiles:
active: prof1
# profile配置
# 启动项目时加载profile java -jar a.jar --spring.profiles.active=prof1
--- (三个行线之间写profile配置)
spring:
profiles: prof1
eureka:
instance:
hostname: eureka
client:
register-with-eureka: true #profile的配置会覆盖公用配置
fetch-registry: true #profile的配置会覆盖公用配置
service-url:
defaultZone: http://eureka1:2001/eureka #eureka启动时向eureka1注册
---
> profile 配置名
dev, test, prod,
## 上下文对象 Context
Spring Context, Spring 上下文对象, 对象池
创建的所有对象, 实例
程序中 通用/共用 资源 (各个组件中要使用的数据)
## bootstartp.yml 和 application.yml
bootstrap.yml 引导配置
application.yml 应用配置
优先级: bootstrap.yml > application.yml > tomcat服务 >
msapro-note
最新推荐文章于 2024-10-02 08:45:00 发布