Spring Cloud Bus自定義事件踩坑

本文基於Spring Cloud Greenwich.SR3,Spring Boot 2.1.10.RELEASEjava

偶然發現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 "發送成功!";
    }
}
相關文章
相關標籤/搜索