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
BinderBinder是应用与消息中间件之间的封装,目前实行了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文件,在文件中acceptLocalacceptRemote方法上打上断点,看下代码是怎么走的。
在这里插入图片描述
2.自己根据1先研究下源码,然后在网上看下大神写的文章干货|Spring Cloud Bus 消息总线介绍 ,看完后自己再捋一下源码。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值