SpringCloudStream、Spring事件监听机制、SpringCloudBus
一、SpringCloudStream
1.什么是SpringCloudStream
官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架。
应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们的配置来 binding ,而 Spring Cloud Stream 的 binder 负责与消息中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。
通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。目前仅支持RabbitMQ、Kafka。
一句话解释:屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
2.SpringCloudStream中的几个概念
应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们配置来 binding ,而 Spring Cloud Stream 的 binder 负责与中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。
springcloudStream 标准流程套路:
组成 | 说明 |
---|---|
Middleware | 中间件,目前只支持RabbitMQ和Kafka |
Binder | Binder是应用与消息中间件之间的封装,目前实行了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现 |
@Input | 注解标识输入通道,通过该输入通道接收到的消息进入应用程序 |
@Output | 注解标识输出通道,发布的消息将通过该通道离开应用程序 |
@StreamListener | 监听队列,用于消费者的队列的消息接收 |
@EnableBinding | 指信道channel和exchange绑定在一起 |
Binder: 很方便的连接中间件,屏蔽差异
Channel: 是队列的一种抽象,在消息通讯系统中就是实现存储和转发的媒介
Source、Sink: 简单的可理解为参照对象是SpringCloudStream自身,从Stream发布消息就是输出,接收消息就是输入。
3.环境搭建(基于rabbitMQ)
1.准备三个项目(admin、feign、workflow),一个消息生产者(workflow),两个消息消费者(admin、feign),这三个模块都注册到了注册中心,注册中心相关代码就不贴了。项目名没什么意义忽略就行
2.pom.xml。因为bus包含了stream,所以我们直接引入了bus的依赖(有这个概念就行,后面会说到bus)
<!-- 消息总线rabbitmq支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
3.workflow
(1)application.yml
spring:
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #配置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 47.×××.×××.×××
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
output: #这个名字是一个通道的名称
destination: studyExchange #表示使用的exchange名称定义
content-type: application/json #设置消息类型,本次为json,文本则设置"text/plain"
binder: defaultRabbit #设置要绑定的消息服务的具体设置
(2)代码
public interface IMessageProvider {
String send();
}
--------------------------------------------------------------------------------------------------
@Slf4j
@EnableBinding(Source.class) //定义消息的推送管道
public class MessageProviderImpl implements IMessageProvider {
@Autowired
private MessageChannel output; //消息发送管道
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
log.info("*******serial:" + serial);
return null;
}
}
--------------------------------------------------------------------------------------------------
@RestController
@RequestMapping("/api/sendMessage")
public class SendMessageController {
@Autowired
private IMessageProvider messageProvider;
@GetMapping
public String sendMessage() {
return messageProvider.send();
}
}
4.admin、feign分别为消费者一号和消费者二号
(1)application.yml(两个模块配置一样)
spring:
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #配置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 47.×××.×××.×××
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
input: #这个名字是一个通道的名称
destination: studyExchange #表示使用的exchange名称定义
content-type: application/json #设置消息类型,本次为json,文本则设置"text/plain"
binder: defaultRabbit #设置要绑定的消息服务的具体设置
(2)代码
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
@Value("${server.port}")
private String port;
@StreamListener(Sink.INPUT)
public void input(Message<String> message) {
System.out.println("消费者1号,----->接收到的消息:" + message.getPayload() + "\t port:" + port);
}
}
5.启动项目调用消息生产者 api/sendMessage
workflow:
admin:
feign:
当生产者发布消息后,消费者都收到了对应的消息。
4.group概念
由3可以看出,使用springCloudStream很简单的就实现了消息传输,如果消息中间件想替换成kafka我们只需要换成kafka的依赖并且改动下配置文件就可以了,不需要对代码进行改动,这样就实现了无感切换消息中间件。
但根据上述的方式进行配置会有两个问题:
1.消息持久化。假如workflow发布了消息,但是admin和feign项目挂了,这样就会出现消息的丢失。
2.消息重复消费。一般生产环境都会搭建集群,假如admin和feign是同一个集群里的不同节点,消息就被重 复消费了。
想要解决这两个问题就需要使用springCloudStream的消息分组概念。
在stream中处于同一个group中的多个消费者是竞争关系,这样就能够保证消息只会被其中一个应用消费一次。即:不同组是可以全面消费的(重复消费), 同一组内会发生竞争关系,只有其中一个可以消费。
修改消费者配置文件,admin和fegin都修改成下面的配置:
spring:
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #配置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 47.×××.×××.×××
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
input: #这个名字是一个通道的名称
destination: studyExchange #表示使用的exchange名称定义
content-type: application/json #设置消息类型,本次为json,文本则设置"text/plain"
binder: defaultRabbit #设置要绑定的消息服务的具体设置
group: sz1
workflow发布三次消息:
admin和feign:
这样就解决了消息持久化和重复消费问题。到这里可能就有人问消息持久化的问题解决了吗?是怎么解决的?
其实是这样的,如果你配置了group的话,针对rabbitmq其实就相当于创建了一个队列,如下图
这个队列不会因为项目停止而销毁;如果没有配置group,在启动项目的时候每个消费者项目都会创建一个随机命名的队列,随机命名的队列会随项目的停止而销毁,所以就不能实现持久化。
二、spring事件监听机制
因为SpringCloudBus用到了SpringCloudStream和spring事件监听机制,所有我们需要先理解一、二的内容,才能更好的理解SpringCloudBus。
1.spring事件构成
Spring中的事件机制其实就是设计模式中的观察者模式,主要由以下角色构成:
1.事件
ApplicationEvent:表示事件本身,自定义事件需要继承该类,可以用来传递数据。
2.事件发布者(发布事件)
ApplicationEventPublisherAware:事件发送器,通过实现这个接口,来触发事件。
3.事件监听器(监听并处理事件)
ApplicationListener:事件监听器接口,事件的业务逻辑封装在监听器里面。
2.同步事件和异步事件
同步事件:在一个线程里,按顺序执行业务,做完一件事再去做下一件事。
异步事件:在一个线程里,做一个事的同事,可以另起一个新的线程执行另一件事,这样两件事可以同时执行。
用一个例子来解释同步事件和异步事件的使用场景,有时候一段完整的代码逻辑,可能分为几部分,拿最常见的注册来说,假设完整流程是,1.点击注册->2.检验信息并存库->3.发送邮件通知->4.返回给用户.代码这么写是正确,但不是最好的,缺点如下:
1.逻辑复杂,业务耦合,我们把校验数据并存库和发送邮件写到一个大的业务方法里了,发邮件我们可以看做一个相对独立的业务方法。
2.效率低,假设2和3分别需要1秒的时候,那么用户在点击注册2秒后才能看到相应。
同步事件可以解决上面第一个问题,我们把发邮件的方法独立出来,放到事件里执行,这样注册的这个方法就可以只做2操作,完成之后发布一个事件去执行3,可以很好的解决业务耦合的问题。
异步事件可以完美解决以上两个问题,注册方法执行2操作,执行之后发布一个异步事件,另起一个线程执行3操作,注册方法所在的线程可直接返回给用户,这样不仅实现了业务解耦还提高了效率,用户点击注册,1秒后就能看到响应。
3.实现用户注册后发送邮件功能
1.SysUser
@Data
@TableName("sys_user")
public class SysUser {
@TableId
private Long id;
@TableField(value = "USER_NAME")
private String userName;
@TableField(value = "USER_PASSWORD")
private String userPassword;
@TableField(value = "email")
private String email;
@TableField(value = "BIRTHDAY")
private String birthday;
}
2.SysUserService
@Slf4j
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
/**
* 注册用户
*
* @param user 用户信息
* @return
*/
@Transactional(rollbackFor = Exception.class)
public SysUser registeredUser(SysUser user) {
baseMapper.insert(user);
SendMailEventPublisher.publishEvent(user);
log.info("注册完成!");
return user;
}
}
3.SysUserController
@RestController
@RequestMapping("/api/admin/user")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@GetMapping("/registered")
public ResponseEntity<SysUser> registeredUser(SysUser user) {
return ResponseEntity.ok(sysUserService.registeredUser(user));
}
}
4.SendMailEvent(事件定义)
/**
* @author
* @version 1.0
* @description 注册用户后发送邮件
* @date 2020/4/24 11:58
*/
public class SendMailEvent extends ApplicationEvent {
private SysUser user;
public SendMailEvent(SysUser source) {
super(source);
this.user = source;
}
}
5.SendMailEventListener(事件监听)
@Component
@Slf4j
public class SendMailEventListener implements ApplicationListener<SendMailEvent> {
@Override
public void onApplicationEvent(SendMailEvent sendMailEvent) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SysUser user = (SysUser) sendMailEvent.getSource();
log.info("================用户信息:" + user.toString() + "================");
log.info("发送邮件:" + user.getEmail());
}
}
6.SendMailEventPublisher(事件发布)
@Component
public class SendMailEventPublisher implements ApplicationEventPublisherAware {
private static ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
SendMailEventPublisher.applicationEventPublisher = applicationEventPublisher;
}
public static void publishEvent(SysUser user) {
applicationEventPublisher.publishEvent(new SendMailEvent(user));
}
}
访问 /api/admin/user/registered?userName=sz&userPassword=123&email=123@qq.com,控制台打印:
2020-04-24 18:42:09.042 DEBUG 12752 --- [nio-8763-exec-1] c.s.a.persistence.SysUserMapper.insert : ==> Preparing: INSERT INTO sys_user ( id, USER_PASSWORD, USER_NAME, email ) VALUES ( ?, ?, ?, ? )
2020-04-24 18:42:09.065 DEBUG 12752 --- [nio-8763-exec-1] c.s.a.persistence.SysUserMapper.insert : ==> Parameters: 1253635331194892289(Long), 123(String), sunzhen(String), 123@qq.com(String)
2020-04-24 18:42:09.068 DEBUG 12752 --- [nio-8763-exec-1] c.s.a.persistence.SysUserMapper.insert : <== Updates: 1
2020-04-24 18:42:12.070 INFO 12752 --- [nio-8763-exec-1] c.sz.admin.event.SendMailEventListener : ================用户信息:SysUser(id=1253635331194892289, userName=sz, userPassword=123, email=123@qq.com, birthday=null)================
2020-04-24 18:42:12.070 INFO 12752 --- [nio-8763-exec-1] c.sz.admin.event.SendMailEventListener : 发送邮件:123@qq.com
2020-04-24 18:42:12.071 INFO 12752 --- [nio-8763-exec-1] com.sz.admin.service.SysUserService : 注册完成!
先触发了发送邮件的事件,其后才打印注册完成,可以发现这是一个同步事件的触发,如果想用注解方式实现监听和异步监听可以对SendMailEventListener.class文件的代码做如下改动:
@Component
@Slf4j
public class SendMailEventListener{
@EventListener(SendMailEvent.class)
@Async
public void onApplicationEvent(SendMailEvent sendMailEvent) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SysUser user = (SysUser) sendMailEvent.getSource();
log.info("================用户信息:" + user.toString() + "================");
log.info("发送邮件:" + user.getEmail());
}
}
实现异步还要开启异步的支持,在启动类上增加@EnableAsync注解。
再次访问 /api/admin/user/registered?userName=sz&userPassword=123&email=123@qq.com,控制台打印:
2020-04-24 18:44:31.693 DEBUG 32332 --- [nio-8763-exec-1] c.s.a.persistence.SysUserMapper.insert : ==> Preparing: INSERT INTO sys_user ( id, USER_PASSWORD, USER_NAME, email ) VALUES ( ?, ?, ?, ? )
2020-04-24 18:44:31.714 DEBUG 32332 --- [nio-8763-exec-1] c.s.a.persistence.SysUserMapper.insert : ==> Parameters: 1253635929508163586(Long), 123(String), sunzhen(String), 123@qq.com(String)
2020-04-24 18:44:31.718 DEBUG 32332 --- [nio-8763-exec-1] c.s.a.persistence.SysUserMapper.insert : <== Updates: 1
2020-04-24 18:44:31.725 INFO 32332 --- [nio-8763-exec-1] com.sz.admin.service.SysUserService : 注册完成!
2020-04-24 18:44:34.730 INFO 32332 --- [ask-scheduler-1] c.sz.admin.event.SendMailEventListener : ================用户信息:SysUser(id=1253635929508163586, userName=sz, userPassword=123, email=123@qq.com, birthday=null)================
2020-04-24 18:44:34.730 INFO 32332 --- [ask-scheduler-1] c.sz.admin.event.SendMailEventListener : 发送邮件:123@qq.com
三、SpringCloudBus
spring cloud是按照spring的配置对一系列微服务框架的集成,spring cloud bus是其中一个微服务框架,用于实现微服务之间的通信。
spring cloud bus整合 java的事件处理机制和消息中间件消息的发送和接受,主要由发送端、接收端和事件组成。针对不同的业务需求,可以设置不同的事件,发送端发送事件,接收端接受相应的事件,并进行相应的处理。
Spring Cloud Bus 自身内容还是比较少的,不过还是需要提前了解 Spring Cloud Stream 体系以及 Spring 自身的事件机制,在此基础上,才能更好地理解 Spring Cloud Bus 对本地事件和远程事件的处理逻辑。
目前 Bus 内置的远程事件较少,大多数为配置相关的事件,我们可以继承 RemoteApplicationEvent 并配合 @RemoteApplicationEventScan 注解构建自身的微服务消息体系。
其实感觉解释太多也不一定能理解到底什么是bus,所以不多说了,直接实操一下。
bus自定义事件实践
1.准备项目
eureka(注册中心)、workflow(生产者)、admin(消费者)、feign(消费者),关于生产者和消费者只是业务上的解释,在技术层面他们每个都即可做为生产者也可作为消费者。
workflow
(1)应用入口class需要加上注解
@RemoteApplicationEventScan(basePackageClasses = WorkflowCustomRemoteEvent.class)
(2)自定义远程事件
@Data
public class WorkflowMessageCO {
private UUID entityOid;
private String entityType;
private Integer status;
private Long userId;
private String remark;
private Long documentId;
private String approvalText;
private Long documentTypeId;
private String documentTypeCode;
}
------------------------------------------------------------------------------------------------
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonIgnoreProperties("source")
@Data
public class WorkflowCustomRemoteEvent extends RemoteApplicationEvent {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss:SSS");
private WorkflowMessageCO workflowMessage;
public WorkflowCustomRemoteEvent() {
}
public WorkflowCustomRemoteEvent(Object source, String originService, String destinationService, WorkflowMessageCO workflowMessage) {
super(source, originService, destinationService);
this.workflowMessage = workflowMessage;
}
@Override
public String toString() {
return "WorkflowCustomRemoteEvent{WorkflowMessageCO=" + this.workflowMessage
+ ",eventId:" + super.getId()
+ ",originService:" + super.getOriginService()
+ ",destinationService:" + super.getDestinationService()
+ ",time:" + simpleDateFormat.format(new Date(super.getTimestamp()))
+ '}';
}
}
(3)发布事件
@RestController
@RequestMapping("/api/workflow")
@Slf4j
public class WorkflowEventPublisherController {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@GetMapping("/event/publisher")
public void workflowEventPublisher() {
WorkflowMessageCO workflowMessageCO = new WorkflowMessageCO();
workflowMessageCO.setStatus(1002);
workflowMessageCO.setDocumentId(18273829212321123L);
workflowMessageCO.setRemark("单据编号:" + "EXP12312321471222201");
WorkflowCustomRemoteEvent workflowCustomRemoteEvent = new WorkflowCustomRemoteEvent(this, "workflow:**", "admin", workflowMessageCO);
log.info("[发布工作流事件消息]:" + workflowCustomRemoteEvent);
applicationEventPublisher.publishEvent(workflowCustomRemoteEvent);
}
}
(4)消息被消费后接收确认事件
@Component
@Slf4j
public class WorkflowCustomRemoteEventListener {
@EventListener(AckRemoteApplicationEvent.class)
public void ackConsumerConfirm(AckRemoteApplicationEvent event) {
// 相当于回调函数,在消息成功被消费时调用,event里能取得actId: event.getAckId 和 消息的ID:event.getId()
log.info("[工作流事件消息确认]消费者端: " + event.getOriginService() + ", 服务消费确认 ackId :" + event.getAckId());
}
}
(5)配置文件
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #配置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 47.×××.×××.×××
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
# output: #这个名字是一个通道的名称
# destination: studyExchange #表示使用的exchange名称定义
# content-type: application/json #设置消息类型,本次为json,文本则设置"text/plain"
# binder: defaultRabbit #设置要绑定的消息服务的具体设置
springCloudBusInput:
destination: sz
group: workflow
springCloudBusOutput:
destination: sz
bus:
enabled: true
trace:
enabled: true
admin
(1)应用入口class需要加上注解
@RemoteApplicationEventScan(basePackageClasses = WorkflowCustomRemoteEvent.class)
(2)事件监听接口
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonIgnoreProperties({"source"})
@Data
public class WorkflowCustomRemoteEvent extends RemoteApplicationEvent {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss:SSS");
private WorkflowMessageCO workflowMessage;
public WorkflowCustomRemoteEvent() {
}
public WorkflowCustomRemoteEvent(Object source, String originService, String destinationService, WorkflowMessageCO workflowMessage) {
super(source, originService, destinationService);
this.workflowMessage = workflowMessage;
}
@Override
public String toString() {
return "WorkflowCustomRemoteEvent{WorkflowMessageCO=" + this.workflowMessage
+ ",eventId:" + super.getId()
+ ",originService:" + super.getOriginService()
+ ",destinationService:" + super.getDestinationService()
+ ",time:" + simpleDateFormat.format(new Date(super.getTimestamp()))
+ '}';
}
}
--------------------------------------------------------------------
public interface WorkflowEventConsumerInterface {
@EventListener({WorkflowCustomRemoteEvent.class})
void workFlowConsumer(WorkflowCustomRemoteEvent event);
}
(3)实现监听接口
@RestController
@Slf4j
public class WorkflowEventConsumer implements WorkflowEventConsumerInterface {
@Override
public void workFlowConsumer(WorkflowCustomRemoteEvent event) {
log.info("[工作流事件消息接收]" + event.toString());
}
}
(4)配置文件
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #配置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 47.101.130.154
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
springCloudBusInput:
destination: sz
group: admin
springCloudBusOutput:
destination: sz
bus:
enabled: true
trace:
enabled: true
fegin
前三步和admin一致
配置文件如下:
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #配置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 47.101.130.154
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
springCloudBusInput:
destination: sz
group: order-service
springCloudBusOutput:
destination: sz
bus:
enabled: true
trace:
enabled: true
2.workflow发布事件
访问 /api/workflow/event/publisher
3.结果
workflow
2020-05-06 23:29:49.302 INFO 1452 --- [nio-8800-exec-1] c.s.w.c.WorkflowEventPublisherController : [发布工作流事件消息]:WorkflowCustomRemoteEvent{WorkflowMessageCO=WorkflowMessageCO(entityOid=null, entityType=null, status=1002, userId=null, remark=单据编号:EXP12312321471222201, documentId=18273829212321123, approvalText=null, documentTypeId=null, documentTypeCode=null),eventId:2e91bec0-3881-42ef-9847-84b4fa38dcd4,originService:workflow:**,destinationService:admin:**,time:2020-05-06-23:29:49:302}
2020-05-06 23:29:49.562 INFO 1452 --- [ sz.workflow-1] .s.w.e.WorkflowCustomRemoteEventListener : [工作流事件消息确认]消费者端: admin:8763:53c2aba43bdb3c15ae0c7bd860f04afc, 服务消费确认 ackId :2e91bec0-3881-42ef-9847-84b4fa38dcd4
admin
2020-05-06 23:29:49.438 INFO 23704 --- [ sz.admin-1] c.s.a.workflow.WorkflowEventConsumer : [工作流事件消息接收]WorkflowCustomRemoteEvent{WorkflowMessageCO=WorkflowMessageCO(entityOid=null, entityType=null, status=1002, userId=null, remark=单据编号:EXP12312321471222201, documentId=18273829212321123, approvalText=null, documentTypeId=null, documentTypeCode=null),eventId:2e91bec0-3881-42ef-9847-84b4fa38dcd4,originService:workflow:**,destinationService:admin:**,time:2020-05-06-23:29:49:302}
fegin没有打印信息,如果想要有的话,把workflow第(3)步中的admin改为order-service后再次访问 /api/workflow/event/publisher,即可看到打印结果。因为fegin注册到eureka上的名字叫order-service。
4.底层分析
到这里为止,我们就实现了bus自定义事件,但是有的人估计还是很懵,不明白是如何实现的,这里我有两个建议。
1.分别在workflow、admin、fegin项目中找到BusAutoConfiguration.java文件,在文件中acceptLocal和acceptRemote方法上打上断点,看下代码是怎么走的。
2.自己根据1先研究下源码,然后在网上看下大神写的文章干货|Spring Cloud Bus 消息总线介绍 ,看完后自己再捋一下源码。