Spring Cloud Bus
Spring Cloud Bus将分布式系统的节点与轻量级消息代理链接。这可以用于广播状态更改(例如配置更改)或其他管理指令。一个关键的想法是,总线就像一个分布式执行器,用于扩展的Spring Boot应用程序,但也可以用作应用程序之间的通信通道。目前唯一的实现是使用AMQP代理作为传输,但是相同的基本功能集(还有一些取决于传输)在其他传输的路线图上。
注意 | Spring Cloud根据非限制性Apache 2.0许可证发布。如果您想为文档的这一部分做出贡献,或者发现错误,请在github中找到项目中的源代码和问题跟踪器。 |
---|
快速开始
Spring Cloud Bus的工作原理是添加Spring Boot自动配置,如果它在类路径中检测到自身。所有您需要做的是启用总线是将spring-cloud-starter-bus-amqp或spring-cloud-starter-bus-kafka添加到您的依赖关系管理中,并且Spring Cloud负责其余部分。确保代理(RabbitMQ或Kafka)可用和配置:在本地主机上运行,??您不应该做任何事情,但如果您远程运行使用Spring Cloud连接器或Spring Boot定义经纪人凭据的约定,例如Rabbit
application.yml
spring:
rabbitmq:
host: mybroker.com
port: 5672
username: user
password: secret
总线当前支持向所有节点发送消息,用于特定服务的所有节点(由Eureka定义)。未来可能会添加更多的选择器标准(即,仅数据中心Y中的服务X节点等)。/bus/*执行器命名空间下还有一些http端点。目前有两个实施。第一个/bus/env发送密钥/值对来更新每个节点的Spring环境。第二个,/bus/refresh,将重新加载每个应用程序的配置,就好像他们在他们的/refresh端点上都被ping过。
注意 | 总线起动器覆盖了Rabbit和Kafka,因为这是两种最常用的实现方式,但是Spring Cloud Stream非常灵活,绑定器将与spring-cloud-bus结合使用。 |
---|
处理实例
HTTP端点接受“目的地”参数,例如“/ bus / refresh?destination = customers:9000”,其中目的地是ApplicationContext ID。如果ID由总线上的一个实例拥有,那么它将处理消息,所有其他实例将忽略它。Spring Boot将ContextIdApplicationContextInitializer中的ID设置为spring.application.name,活动配置文件和server.port的组合。
寻址服务的所有实例
“destination”参数用于Spring PathMatcher(路径分隔符为冒号:)以确定实例是否处理该消息。使用上述示例,“/ bus / refresh?destination = customers:**”将针对“客户”服务的所有实例,而不管配置文件和端口设置为ApplicationContext ID。
应用程序上下文ID必须是唯一的
总线尝试从原始ApplicationEvent一次消除处理事件两次,一次从队列中消除。为此,它会检查发送应用程序上下文id,以重新显示当前的应用程序上下文ID。如果服务的多个实例具有相同的应用程序上下文id,则不会处理事件。在本地机器上运行,每个服务将在不同的端口上,这将是应用程序上下文ID的一部分。Cloud Foundry提供了区分的索引。要确保应用程序上下文ID是唯一的,请将spring.application.index设置为服务的每个实例唯一的值。例如,在lattice中,在application.properties中设置spring.application.index=${INSTANCE_INDEX}(如果使用configserver,请设置bootstrap.properties)。
自定义Message Broker
Spring Cloud Bus使用 Spring Cloud Stream广播消息,以便获取消息流,只需要在类路径中包含您选择的binder实现。有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是使用外部中间件的主题的名称)。通常,默认值就足够了。
要更多地了解如何自定义消息代理设置,请参阅Spring Cloud Stream文档。
跟踪Bus Events
可以通过设置spring.cloud.bus.trace.enabled=true来跟踪总线事件(RemoteApplicationEvent的子类)。如果这样做,那么Spring Boot TraceRepository(如果存在)将显示每个发送的事件和来自每个服务实例的所有ack。示例(来自/trace端点):
{
"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收到(acked)。
为了处理信号,您可以向您的应用添加AckRemoteApplicationEvent和SentApplicationEvent类型的@EventListener(并启用跟踪)。或者您可以利用TraceRepository并从中挖掘数据。
注意 | 任何总线应用程序都可以跟踪ack,但有时在一个可以对数据进行更复杂查询的中央服务器中执行此操作是有用的。或者将其转发到专门的跟踪服务。 |
---|
广播自己的Events
总线可以携带任何类型为RemoteApplicationEvent的事件,但默认传输是JSON,并且解串器需要知道哪些类型将提前使用。要注册一个新类型,它需要在org.springframework.cloud.bus.event的子包中。
要自定义事件名称,您可以在自定义类上使用@JsonTypeName,或者依赖默认策略来使用类的简单名称。请注意,生产者和消费者都需要访问类定义。
在自定义包中注册事件
如果您不能或不想为自定义事件使用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程序包将通过在@RemoteApplicationEventScan上明确指定程序包来注册。请注意,您可以指定要扫描的多个基本软件包。
欢迎关注作者的公众号《Java编程生活》,每日记载Java程序猿工作中遇到的问题