Spring Cloud Bus 源码分析

Spring Cloud Bus使用轻量级消息代理将分布式系统的节点连接起来。然后可以使用此广播状态更改(例如配置更改)或其他管理指令。 Spring Cloud 体系中的一个组件 Spring Cloud Bus ,要分析Spring Cloud Bus,建议先熟悉 Spring Cloud Stream,不然无法理解 Spring Cloud Bus 内部的代码。

Spring Cloud Stream 源码分析点击这里。

要使用Bus,首先需要在pom文件中引用。

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>

在spring-cloud-starter-bus-amqp中,可以看到又依赖了Spring Stream组件spring-cloud-starter-stream-rabbit和spring-cloud-bus。

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-bus</artifactId>
		</dependency>
	</dependencies>

Stream已经分析过了,直接看spring-cloud-bus组成。看起来内容好少。

在spring.factories文件中

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.bus.BusAutoConfiguration,\
org.springframework.cloud.bus.jackson.BusJacksonAutoConfiguration

# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.bus.BusEnvironmentPostProcessor

直接看BusAutoConfiguration。

@Configuration
@ConditionalOnBusEnabled
@EnableBinding({SpringCloudBusClient.class})
@EnableConfigurationProperties({BusProperties.class})
public class BusAutoConfiguration implements ApplicationEventPublisherAware {
    public static final String BUS_PATH_MATCHER_NAME = "busPathMatcher";
    @Autowired
    @Output("springCloudBusOutput")
    private MessageChannel cloudBusOutboundChannel;
    @Autowired
    private ServiceMatcher serviceMatcher;
    @Autowired
    ..................
}

又看到了@EnableBinding注解,这次注解的value为SpringCloudBusClient,作用类似我们之前在Stream中自己的StreamSendClient测试实例。

public interface SpringCloudBusClient {
    String INPUT = "springCloudBusInput";
    String OUTPUT = "springCloudBusOutput";

    @Output("springCloudBusOutput")
    MessageChannel springCloudBusOutput();

    @Input("springCloudBusInput")
    SubscribableChannel springCloudBusInput();
}

按照之前Steam的源码分析,@EnableBinding(SpringCloudBusClient)这里会创建:

1、BeanName为springCloudBusOutput,生成该Bean的工厂类为SpringCloudBusClient,生成Bean的方法为springCloudBusOutput。

2、BeanName为springCloudBusInput,生成该Bean的工厂类为SpringCloudBusClient,生成Bean的方法为springCloudBusInput。

3、BeanName为SpringCloudBusClient,beanclass为BindableProxyFactory。BindableProxyFactory通过动态代理根据方法返回不同的MessageChannel。

4、并根据BindingServiceConfiguration的Bean对象StreamListenerAnnotationBeanPostProcessor处理输入输出的关联关系。

 

继续看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());
        }

    }

    @StreamListener("springCloudBusInput")
    public void acceptRemote(RemoteApplicationEvent event) {
        if (event instanceof AckRemoteApplicationEvent) {
            if (this.bus.getTrace().isEnabled() && !this.serviceMatcher.isFromSelf(event) && this.applicationEventPublisher != null) {
                this.applicationEventPublisher.publishEvent(event);
            }

        } else {
            if (this.serviceMatcher.isForSelf(event) && this.applicationEventPublisher != null) {
                if (!this.serviceMatcher.isFromSelf(event)) {
                    this.applicationEventPublisher.publishEvent(event);
                }

                if (this.bus.getAck().isEnabled()) {
                    AckRemoteApplicationEvent ack = new AckRemoteApplicationEvent(this, this.serviceMatcher.getServiceId(), this.bus.getAck().getDestinationService(), event.getDestinationService(), event.getId(), event.getClass());
                    this.cloudBusOutboundChannel.send(MessageBuilder.withPayload(ack).build());
                    this.applicationEventPublisher.publishEvent(ack);
                }
            }

            if (this.bus.getTrace().isEnabled() && this.applicationEventPublisher != null) {
                this.applicationEventPublisher.publishEvent(new SentApplicationEvent(this, event.getOriginService(), event.getDestinationService(), event.getId(), event.getClass()));
            }

        }
    }

    @Configuration
    @ConditionalOnClass({Endpoint.class, RefreshScope.class})
    @ConditionalOnBean({ContextRefresher.class})
    protected static class BusRefreshConfiguration {
        protected BusRefreshConfiguration() {
        }

        @Bean
        @ConditionalOnProperty(
            value = {"spring.cloud.bus.refresh.enabled"},
            matchIfMissing = true
        )
        public RefreshListener refreshListener(ContextRefresher contextRefresher) {
            return new RefreshListener(contextRefresher);
        }

        @Configuration
        @ConditionalOnProperty(
            value = {"endpoints.spring.cloud.bus.refresh.enabled"},
            matchIfMissing = true
        )
        protected static class BusRefreshEndpointConfiguration {
            protected BusRefreshEndpointConfiguration() {
            }

            @Bean
            public RefreshBusEndpoint refreshBusEndpoint(ApplicationContext context, BusEndpoint busEndpoint) {
                return new RefreshBusEndpoint(context, context.getId(), busEndpoint);
            }
        }
    }

    @ManagedResource
    public class RefreshBusEndpoint extends AbstractBusEndpoint {
    public RefreshBusEndpoint(ApplicationEventPublisher context, String id, BusEndpoint delegate) {
        super(context, id, delegate);
    }

    @RequestMapping(
        value = {"refresh"},
        method = {RequestMethod.POST}
    )
    @ResponseBody
    @ManagedOperation
    public void refresh(@RequestParam(value = "destination",required = false) String destination) {
        this.publish(new RefreshRemoteApplicationEvent(this, this.getInstanceId(), destination));
    }
}

        @Bean
        @ConditionalOnProperty(
            value = {"spring.cloud.bus.refresh.enabled"},
            matchIfMissing = true
        )
        public RefreshListener refreshListener(ContextRefresher contextRefresher) {
            return new RefreshListener(contextRefresher);
        }

    public class RefreshListener implements ApplicationListener<RefreshRemoteApplicationEvent> {
        private static Log log = LogFactory.getLog(RefreshListener.class);
        private ContextRefresher contextRefresher;

        public RefreshListener(ContextRefresher contextRefresher) {
            this.contextRefresher = contextRefresher;
        }

        public void onApplicationEvent(RefreshRemoteApplicationEvent event) {
            Set<String> keys = this.contextRefresher.refresh();
            log.info("Received remote refresh request. Keys refreshed " + keys);
        }
    }

acceptLocal方法上有@EventListener注解,会将该方法注入到spring监听中。

同时我们可以看到acceptRemote方法上也有@StreamListener("springCloudBusInput")注解,说明这里也建立了对rabbitmq信息的监听。

下面介绍一下调用的过程,当我们修改完Spring Config Server服务器的配置文件后,调用 http://localhost:xxxx/actuator/bus/refresh 时,会经历以下步骤:

1、/refresh请求会进入RefreshBusEndpoint.refresh方法,之后会进行推送

2、进入标有@EventListener的acceptLocal方法,并判断事件是否源自自己,如果是,则通过cloudBusOutboundChannel发送到RabbitMq中。同时还会触发RefreshListener.onApplicationEvent方法进行获取并刷新配置文件。

3、当消息发送到Mq后,会调用所有服务实现了acceptRemote方法(包括eurekaclient1、2、3、...)等等。之后会走这段逻辑

  if (this.serviceMatcher.isForSelf(event) && this.applicationEventPublisher != null) {
                if (!this.serviceMatcher.isFromSelf(event)) {
                    this.applicationEventPublisher.publishEvent(event);
                }
      }

先判断事件是否给自己,再判断事件是否来源于自己。对于eurekaclient来说,事件源于spring config server,所以事件是通知给自己,但是不来源于自己,便会知道需要去从configserver获取新的配置信息。调用applicationEventPublisher.publishEvent(event)方法,触发RefreshListener.onApplicationEvent方法进行获取并刷新配置文件。

以上便是Spring Cloud Bus的大体思路,Bus还是很简单,前提一定是要动Spring Cloud Stream才会更好的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值