Spring Cloud Bus将分布式系统的节点与轻量级消息代理链接。这可以用于广播状态更改(例如配置更改)或其他管理指令。一个关键的想法是,Bus就像一个扩展的Spring Boot应用程序的分布式执行器,但也可以用作应用程序之间的通信渠道。当前唯一的实现是使用AMQP代理作为传输,但是相同的基本功能集(还有一些取决于传输)在其他传输的路线图上。

注意
Spring Cloud根据非限制性Apache 2.0许可证发布。如果您想为文档的这一部分做出贡献,或者发现错误,请在github中找到项目中的源代码和问题跟踪器

快速开始

Spring Cloud Bus通过添加Spring Boot自动配置,如果它在类路径上检测到自己的工作。所有您需要做的是启用总线是将spring-cloud-starter-bus-amqpspring-cloud-starter-bus-kafka添加到您的依赖关系管理中,Spring Cloud负责其余部分。确保代理(RabbitMQ或Kafka)可用和配置:在本地主机上运行您不应该做任何事情,但如果您远程运行使用Spring Cloud Connectors或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过的一样。

注意
Bus初学者涵盖了Rabbit和Kafka,因为这是两个最常见的实现,但Spring Cloud Stream非常灵活,绑定将与spring-cloud-bus结合使用。

处理实例

HTTP端点接受“目的地”参数,例如“/ bus / refresh?destination = customers:9000”,其中目的地是ApplicationContext ID。如果ID由Bus上的一个实例拥有,那么它将处理消息,所有其他实例将忽略它。默认情况下,Spring Boot将ContextIdApplicationContextInitializer中的ID设置为spring.application.name,活动配置文件和server.port的组合。

寻址服务的所有实例

在Spring PathMatcher(路径分隔符为冒号:)中使用“destination”参数来确定实例是否处理该消息。使用上面的示例,“/ bus / refresh?destination = customers:**”将定位“客户”服务的所有实例,而不管配置文件和端口设置为ApplicationContext ID。

应用程序上下文ID必须唯一

总线尝试从原来的ApplicationEvent中消除一次事件两次,一次从队列中消除。为此,它会检查发送应用程序上下文id,以重新显示当前的应用程序上下文ID。如果服务的多个实例具有相同的应用程序上下文ID,则不会处理事件。在本地机器上运行,每个服务将在不同的端口上,这将是应用程序上下文ID的一部分。Cloud Foundry提供了区分的索引。为了确保应用程序上下文ID是唯一的,请将spring.application.index设置为服务的每个实例唯一的值。例如,在格子中,在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自动配置约定,因此例如可以使用spring.rabbitmq.*配置属性更改AMQP代理地址。Spring Cloud Bus在spring.cloud.bus.*中具有少量本机配置属性(例如,spring.cloud.bus.destination是使用外部中间件的主题的名称)。通常,默认值就足够了。

要更多地了解如何自定义消息代理设置,请参阅Spring Cloud Stream文档。

追踪Bus事件

Bus事件(RemoteApplicationEvent的子类)可以通过设置spring.cloud.bus.trace.enabled=true进行跟踪。如果这样做,则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:9000stores:8081收到(确认)。

要处理ack信号,您可以向您的应用添加AckRemoteApplicationEventSentApplicationEvent类型的@EventListener(并启用跟踪)。或者您可以利用TraceRepository并从中挖掘数据。

注意
任何Bus应用程序都可以跟踪ack,但有时在一个可以对数据进行更复杂查询的中央服务器中这样做是有用的。或将其转发到专门的跟踪服务。

广播自己的事件

Bus可以携带任何类型为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上的valuebasePackagesbasePackageClasses属性明确指定要扫描的软件包。例如:

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上明确指定包来注册。请注意,您可以指定要扫描的多个基本软件包。