关于使用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());
}
}