关于使用SpringCloudBus消息总线升级SpringBoot2的自定义事件RomoteApplicationEvent事件监听不到的问题

我的项目是用Kafka作为SpringCloudBus的消息总线,来实现事件发送和监听机制。在项目升级SpringBoot到2.0.6的时候,发现事件发送失效了,在kafka的topic里也没找到任何消息,说明这个消息没有发送到kafka里,而是可能事件在发送的过程中因为某些原因失败了。在网上找了一些资料,终于找到了问题所在。
我们定位到BusAutoConfiguration这个类里,找到这个方法:

@EventListener(
    classes = {RemoteApplicationEvent.class}
)
public void acceptLocal(RemoteApplicationEvent event) {
    if (this.serviceMatcher.isFromSelf(event) && !(event instanceof AckRemoteApplicationEvent)) {
        this.cloudBusOutboundChannel.send(MessageBuilder.withPayload(event).build());
    }

}

这个就是监听我们的自定义事件“RemoteApplicationEvent”,所以我们的事件为什么会发送不出去,可以从这里断点看看原因。
这里我们可以来走一遍,首先定义的事件是这个:

@NoArgsConstructor
public class TestEvent extends RemoteApplicationEvent {

    private String message;

    public TestEvent(Object source, String originService, String destinationService, String message) {
        super(source, originService, destinationService);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

“originService”就是事件的发送方,填的一般是applicationId,然后事件发送如下:

@RestController
public class TestController {

    private ApplicationContext context;


    @Autowired
    public TestController(ApplicationContext context) {
        this.context = context;
    }

    @GetMapping("/v1/test")
    @Permission(level = ResourceLevel.USER, permissionWithin = true, permissionPublic = true)
    public void test(){
        context.publishEvent(new TestEvent(this, context.getId(), null,"test"));
    }
}

这里我们的“originService”填的是“context.getId()”,发送一个事件,来到了刚刚打断点的地方,我们直接看第一个判断方法“isFromSelf”:

"originService"就是我们传的contextId,而这里的“serviceId”是SpringCloudBus自动生成的id,所以我们传的contextId就跟这个不匹配了,事件发送方都对应不上,就不给你发送事件了。
当然这个Bus生成的id我们可以获取到,这个是BusProperties自动配置的,生成规则是你的服务id加唯一标识串,我们将originService修改一下:

@RestController
public class TestController {

    private ApplicationContext context;

    @Autowired
    private BusProperties busProperties;
    @Autowired
    public TestController(ApplicationContext context) {
        this.context = context;
    }

    @GetMapping("/v1/test")
    @Permission(level = ResourceLevel.USER, permissionWithin = true, permissionPublic = true)
    public void test(){
        context.publishEvent(new TestEvent(this, busProperties.getId(), null,"test"));
    }
}

这样问题就解决了,当然这个id也可以由我们自己配置:

spring:
  application:
    name: ta-test
  cloud:
    bus:
      id: ta-test1

配置生效后就是用我们这个id去识别了:

判断事件有没有发送成功,我们就要去消息队列里确认消息有没有发送成功,如果发送成功了,那就是服务消费方的问题了,比如可能没有加“@RemoteApplicationEventScan”这个扫描,或者是你的监听类没有加“@Component”等等。

@Component
public class TestEventListener {

    @EventListener
    public void onApplicationEvent(TestEvent testEvent) {
        System.out.println(testEvent.getMessage());
    }
}
posted @ 2020-12-14 16:48  Conwie  阅读(1113)  评论(0编辑  收藏  举报