This paper is based on Spring Cloud Greenwich.SR3, Spring Boot 2.1.10.RELEASE
By chance, I found that Spring Cloud also has this component. By combining with Message Oriented Middleware (RabbitMQ or Kafka), the published Spring events can be passed to other JVM projects. It feels very good, so I wrote a demo to try. The code can Reference here PS: this code is after pit filling.
As a result, no matter how you try, RabbitMQ's console can't observe the message passing, which means that no message is sent from the project side.
What's more, if you search the tutorial of Bus on Google, it's all Spring Cloud Config related. It's useless
Finally, I found a piece of code in 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()); } }
It seems that Spring will have a judgment before publishing events to MQ
public boolean isFromSelf(RemoteApplicationEvent event) { String originService = event.getOriginService(); String serviceId = getServiceId(); return this.matcher.match(originService, serviceId); } public String getServiceId() { return this.id; }
After debugging, it is found that the originService=producer-1, serviceId=producer-8111-random string here is obviously not equal.
Here, the id is assigned through the construction method. The only place for the whole project to call the construction method is in the BusAutoConfiguration configuration
@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; }
Obviously, the configuration file BusProperties is read here. However, you will find that no matter what the id here is configured into, it will change it to the form above. The culprit is as follows
//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 replaced that configuration with a custom ID generation!
Once the cause is found, the solution is simple. When publishing an event, use the BusProperties ID to ensure that the if is true
@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 "Sending succeeded!"; } }