为什么80%的码农都做不了架构师?>>>
Spring Cloud Bus
总线
Spring Cloud Bus 通过一个轻量级的消息代理把分布式系统中各个节点串联起来。 这样就可以方便的进行广播或者其他管理操作。
其核心概念就类似在一个Spring Boot Actuator的分布式实现之上,并在此基础之上提供应用之间互相通讯的能力。
通讯以AMQP协议为基础,并在此基础之上提供路标跟踪。
6.1 Quick Start
Spring Cloud Bus是通过Spring Boot自动配置机制来加载的,所以只要在classpath中有相应的jar就行。 Spring Cloud提供相应的依赖管理,所以使用时,只需要添加:
spring-cloud-starter-bus-amqp
或者spring-cloud-starter-bus-kafka
就行了。 如果仅是本地运行,那依赖引入后,只需要配置好消息代理(RabbitMQ 或者 Kafka)就行。如果是使用Spring Cloud Connectors远程运行,或者是需要按Spring Boot方式配置密码,则需要手工加入一些配置。比如,使用Rabbit时:
application.yml
spring:
rabbitmq:
host: mybroker.com
port: 5672
username: user
password: secret
总线支持消息发送到所有已监听的节点,或者某个特定服务的所有节点。以后会加入更多的选择策略。 同样的,总线也提供了一些HTTP接口:
/bus/*
。当前版本有两个接口实现,第一个:/bus/env
,发送键值对去更新每个节点的Spring Environment;第二个:/bus/refresh
,重新加载每一个应用的配置信息,类似于/refresh
。
注意: 由于都是最常用的,Bus的starter会自动带上Rabbit和Kafka启动器,但是Spring Cloud Stream是很灵活的,可以很方便的整合到spring-cloud-bus
6.2 Addressing an Instance
HTTP接口接受一个“destination”参数作为一个
ApplicationContext
的ID,例如:/bus/refresh?destination=customers:9000
。如果这个ID的实例在总线上,则会被处理,否则忽略。 Spring Boot可以通过ContextIdApplicationContextInitializer
设置ID,默认ID是由spring.application.name
,配置的分隔符(:
)以及server.port
三部分拼接而成。
6.3 Addressing all instances of a service
"destination"参数会被Spring的
PathMatcher
来决定实例是否需要处理此消息。在上面的例子中,/bus/refresh?destination=customers:**
将会让所有"customers"服务的实例处理,而不按ApplicationContext
的ID来处理。
6.4 Application Context ID must be unique
总线会避免让一个事件重复被处理,一个原始
ApplicationEvent
就像从队列中取出一样。 为了达到这个目的,发送事件时,会检查目标ApplicationContext的ID和当前ApplicationContext的ID。如果服务的多个实例拥有着相同的ApplicationContext ID,事件将不会被处理。本地机器上跑着的每一个服务,都会有一个不同的端口,所以默认ID也会不一样。 Cloud Foundry提供一个索引,来确保ApplicationContext的ID唯一,可以在服务的每一个实例设置spring.application.index
。例如:在application.properties
或者bootstrap.properties
(使用配置服务时)中,设置spring.application.index=${INSTANCE_INDEX}
。
6.5 Customizing the Message Broker
Spring Cloud Bus 使用 Spring Cloud Stream来进行消息广播。Bus提供了方便的starter来适配AMQP(RabbitMQ)和Kafka(
spring-cloud-starter-bus-[amqp,kafka]
)。
通常来说Spring Cloud Stream 依靠Spring Boot 的自动配置机制来进行集成,因此,对于AMQP代理的地址可以通过
spring.rabbitmq.*
等配置项进行设置。Spring Cloud Bus有一些自己的配置项在spring.cloud.bus.*
中(例如:spring.cloud.bus.destination
可以指定topic的名字)。一般用默认的就行了。
更多定制消息代理的设置,可以参考Spring Cloud Stream相关文档。
6.6 Tracing Bus Events
总线事件(都继承于
RemoteApplicationEvent
)是可被追踪的,只需要设置spring.cloud.bus.trace.enabled=true
。配置后,可以在Spring Boot的TraceRepository
(如果有的话)中看到每一个事件的发送以及每一个服务实例的确认信息。例如:调用/trace
接口(Spring Boot Actuator)
{
"timestamp": "2015-11-26T10:24:44.411+0000",
"info": {
"signal": "spring.cloud.bus.ack",
"type": "RefreshRemoteApplicationEvent",
"id": "c4d374b7-58ea-4928-a312-31984def293b",
"origin": "stores:8081",
"destination": "*:**"
}
},
{
"timestamp": "2015-11-26T10:24:41.864+0000",
"info": {
"signal": "spring.cloud.bus.sent",
"type": "RefreshRemoteApplicationEvent",
"id": "c4d374b7-58ea-4928-a312-31984def293b",
"origin": "customers:9000",
"destination": "*:**"
}
},
{
"timestamp": "2015-11-26T10:24:41.862+0000",
"info": {
"signal": "spring.cloud.bus.ack",
"type": "RefreshRemoteApplicationEvent",
"id": "c4d374b7-58ea-4928-a312-31984def293b",
"origin": "customers:9000",
"destination": "*:**"
}
}
追踪信息展示了
RefreshRemoteApplicationEvent
从customers:9000
发送,广播到所有服务,然后被customers:9000
和stores:8081
接收到。
可以自己添加
@EventListener
来处理ack信号,对于应用来说,有两种事件类型:AckRemoteApplicationEvent
和SentApplicationEvent
。还可以直接获得TraceRepository
并从中得到数据。
注意: 任何总线上的应用都可以追踪acks,对于中心服务来说,这样对于数据来说可以进行更复杂的查询。也可以转发到一个特定的追踪服务上。
6.7 Broadcasting Your Own Events
总线可以传输任何
RemoteApplicationEvent
类型,但是默认转换协议是JSON,并且反序列化需要知道使用时的目标类型。当需要注册一个新的事件类型时,可以参见org.springframework.cloud.bus.event
包中的API。 自定义类型时可以通过@JsonTypeName
来指定事件的名字,默认策略只需要制定class的简称就行。注意,事件消息的产生者和消费者都需要能够访问这个class。
6.7.1 Registering events in custom packages
如果不想使用
org.springframework.cloud.bus.event
包中的事件类型,而想要使用自定义事件类型,那必须使用@RemoteApplicationEventScan
指定哪一个包来扫描RemoteApplicationEvent
。@RemoteApplicationEventScan
会扫描子包。
例如:如果自定义了
FooEvent
事件:
package com.acme;
public class FooEvent extends RemoteApplicationEvent {
...
}
可以通过下面的方式来注册这个事件:
package com.acme;
@Configuration
@RemoteApplicationEventScan
public class BusConfiguration {
...
}
如果不指定包,则以
@RemoteApplicationEventScan
的当前包为默认值。在这个例子中,com.acme
就是扫描BusConfiguration
所在的包。
也可以通过这个
@RemoteApplicationEventScan
的value
,basePackages
或者basePackageClasses
等属性来指定要扫描的包。例如:
package com.acme;
@Configuration
//@RemoteApplicationEventScan({"com.acme", "foo.bar"})
//@RemoteApplicationEventScan(basePackages = {"com.acme", "foo.bar", "fizz.buzz"})
@RemoteApplicationEventScan(basePackageClasses = BusConfiguration.class)
public class BusConfiguration {
...
}
上例中,所有的
@RemoteApplicationEventScan
示例都是等价的,com.acme
包都会被扫描。 注意,你可以指定来扫描多个包。