本文基于Spring Cloud Greenwich.SR3,Spring Boot 2.1.10.RELEASE
java
偶然发现Spring Cloud还有这个组件,经过结合消息中间件(RabbitMQ或者Kafka),使得发布的Spring事件能够传递给其余JVM项目,感受很不错,就写了个demo试了一下,代码能够参考这里(PS:这个代码是填坑以后的)。git
结果不管怎么试,RabbitMQ的控制台都观察不到消息传递,说明项目那边没有发出任何消息。spring
更坑爹的是,若是你在Google上搜索Bus的教程,所有都是Spring Cloud Config相关,没一点有用的......app
最后我在BusAutoConfiguration中发现了这么一段代码dom
@EventListener(classes = RemoteApplicationEvent.class) public void acceptLocal(RemoteApplicationEvent event) { if (this.serviceMatcher.isFromSelf(event) && !(event instanceof AckRemoteApplicationEvent)) { this.cloudBusOutboundChannel.send(MessageBuilder.withPayload(event).build()); } }
看来Spring在将事件发布到MQ以前,会有一个判断ide
public boolean isFromSelf(RemoteApplicationEvent event) { String originService = event.getOriginService(); String serviceId = getServiceId(); return this.matcher.match(originService, serviceId); } public String getServiceId() { return this.id; }
而后通过调试发现,这里的originService=producer-1,serviceId=producer-8111-随机字符串,很明显是不相等的。post
这里id的赋值是经过构造方法进行的,整个工程构造方法调用的惟一一个地方就在BusAutoConfiguration这个配置里ui
@Bean public ServiceMatcher serviceMatcher(@BusPathMatcher PathMatcher pathMatcher, BusProperties properties, Environment environment) { String[] configNames = environment.getProperty(CLOUD_CONFIG_NAME_PROPERTY, String[].class, new String[] {}); ServiceMatcher serviceMatcher = new ServiceMatcher(pathMatcher, properties.getId(), configNames); return serviceMatcher; }
很显然这里读取的是BusProperties这个配置文件,然而你会发现,这里的id不管你配成什么,他都会给你改为上边那种形式,罪魁祸首以下所示....this
//BusEnvironmentPostProcessor.java @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Map<String, Object> map = new HashMap<String, Object>(); map.put("spring.cloud.bus.id", getDefaultServiceId(environment)); addOrReplace(environment.getPropertySources(), map); } private String getDefaultServiceId(ConfigurableEnvironment environment) { return "${vcap.application.name:${spring.application.name:application}}:${vcap.application.instance_index:${spring.application.index:${local.server.port:${server.port:0}}}}:${vcap.application.instance_id:${random.value}}"; }
Spring使用自定义的ID生成替换掉了那个配置!调试
缘由找到了以后,解决很简单,在发布事件的时候,使用BusProperties的ID,保证if判断为真便可
@RequestMapping("/hello") @RestController public class HelloController { @Autowired private ApplicationContext applicationContext; @Autowired private BusProperties busProperties; @GetMapping("/test") public String test(@RequestParam("message") String message) { String id = busProperties.getId(); applicationContext.publishEvent(new CustomApplicationEvent(this, id, null, message)); return "发送成功!"; } }