The Spring Cloud Bus
positions itself as a messaging bus within the Spring Cloud
system, using a message broker
to connect all nodes of a distributed system.
The official Reference
documentation for the Bus
is relatively simple, so simple that it doesn’t even have a diagram.
This is the most 2.1.0 version of the Spring Cloud Bus
code structure (less code)
Bus Example Demonstration
Before we analyze the implementation of the Bus, let’s look at two simple examples of using Spring Cloud Bus.
Configuration of all nodes added
The Bus example is relatively simple because the AutoConfiguration
layer of the Bus is configured by default. All you need to do is introduce the Spring Cloud Stream
and Spring Cloud Bus
dependencies for the messaging middleware. After that, all launched applications will use the same Topic to receive and send messages.
The demo for the Bus is already on github: https://github.com/fangjian0423/rocketmq-binder-demo/tree/master/rocketmq-bus-demo. The demo simulates the launch of 5 nodes, and if you add a configuration item to any one of the instances, it will be added to all nodes.
Access the address provided by the Controller of any node to get the configuration (key is hangzhou).
|
|
All nodes return unknown
because the key
of hangzhou
is missing from the configuration of all nodes.
Bus
internally provides EnvironmentBusEndpoint
which is an Endpoint
used to add/update configuration via message broker
.
Visit the url /actuator/bus-env?name=hangzhou&value=alibaba
of any node to add a configuration item (e.g. visit the url
of node1
):
|
|
Then visit all nodes again /bus/env
to get the configuration.
|
|
You can see that all nodes have a new configuration with key
of hangzhou
and the corresponding value
is alibaba
. This configuration item is done through the EnvironmentBusEndpoint
provided by Bus
.
Spring Cloud Config with Bus completes the refresh of all node configurations to describe the previous example (the example in this article is not a refresh, but a new configuration, but the process is the same).
Configuration modification of some nodes
For example, if you specify destination
as rocketmq-bus-node2
on node1
(node2 is configured with spring.cloud.bus.id
as rocketmq-bus-node2:10002
, which matches), make the following configuration changes.
|
|
Access /bus/env
to get the configuration (since the message is sent on node1, Bus also makes configuration changes to the sender’s node node1).
|
|
As you can see, only node1
and node2
have changed their configurations, while the remaining 3 nodes remain unchanged.
In-depth knowledge of bus
Bus Concept Introduction
Events
The remote event RemoteApplicationEvent
is defined in Bus, which inherits from the Spring
event ApplicationEvent
and which currently has 4 specific implementations.
EnvironmentChangeRemoteApplicationEvent
: Remote environment change event. This event is mainly used to receive a data of type Map<String, String> and update it to the Environment in Spring context. The examples in this article use this event in conjunction with EnvironmentBusEndpoint and EnvironmentChangeListener.AckRemoteApplicationEvent
: The remote acknowledgement event, which is sent back to the AckRemoteApplicationEvent acknowledgement event after the remote event is successfully received inside Bus.RefreshRemoteApplicationEvent
: Remote configuration refresh event. Works with @RefreshScope and all configuration classes modified by @ConfigurationProperties annotation for dynamic refreshingUnknownRemoteApplicationEvent
: remote unknown event, which is wrapped in the Bus internal message body if an exception occurs when converting a remote event
There is also a non-RemoteApplicationEvent
event inside Bus - the SentApplicationEvent
message sending event. Logging of remote message sending with Trace
These events work with ApplicationListener
, for example EnvironmentChangeRemoteApplicationEvent
with EnvironmentChangeListener
for adding/modifying configuration.
|
|
After receiving the EnvironmentChangeRemoteApplicationEvent
event from other nodes, we call EnvironmentManager#setProperty
to set the configuration, which internally sends an EnvironmentChangeEvent
event for each configuration item. EnvironmentChangeEvent
event for each configuration item, which is then listened to by ConfigurationPropertiesRebinder
for rebind
operation to add/update the configuration.
Actuator Endpoint
Bus exposes 2 Endpoint
s internally, EnvironmentBusEndpoint
and RefreshBusEndpoint
, for adding/modifying configuration and global configuration refreshing. Their corresponding Endpoint id
i.e. url
is bus-env
and bus-refresh
.
Configuration
Bus
for message delivery must involve Topic
, Group
and so on. These are encapsulated in BusProperties
, whose default configuration prefix is spring.cloud.bus
.
For example.
spring.cloud.bus.refresh.enabled
is used to enable/disable the Listener for global refresh.spring.cloud.bus.env.enabled
Enable/disable Endpoint for configuration addition/modification.spring.cloud.bus.ack.enabled
Enable/disable sending of -AckRemoteApplicationEvent
events.spring.cloud.bus.trace.enabled
is used to enable/disable the Listener for message logging Trace.
The default Topic used for sending is springCloudBus, which can be modified by configuration, and the Group can be set to broadcast mode or use the UUID with offset of lastest.
Each Bus application has a corresponding Bus id, which is officially taken in the following complex way.
|
|
It is recommended to configure the Bus id manually, as the destination in the Bus remote event is matched against the Bus id.
|
|
Bus Underlay Analysis
The underlying analysis of Bus involves no more than a few aspects.
- How messages are sent
- How the message is received
- How the destination is matched
- How the next action is triggered after the remote event is received
The BusAutoConfiguration
automation configuration class is modified by @EnableBinding(SpringCloudBusClient.class)
.
The usage of @EnableBinding
is explained in the dry run|Introduction to the Spring Cloud Stream
system and principles, and its value
is SpringCloudBusClient.class
, which creates a DirectChannel for input and output based on the proxy in SpringCloudBusClient
. creates a DirectChannel
for input
and output
based on the proxy.
|
|
The properties of the springCloudBusInput
and springCloudBusOutput
Binding
can be modified through the configuration file (e.g. by modifying the topic):
Sending of incoming messages.
|
|
- Use Spring’s event listening mechanism to listen for all local RemoteApplicationEvent remote events (e.g. bus-env sends EnvironmentChangeRemoteApplicationEvent events locally, bus-refresh sends EnvironmentChangeRemoteApplicationEvent events locally). bus-refresh sends the RefreshRemoteApplicationEvent event locally. These events will be listened to here)
- Determine if the event received locally is not an AckRemoteApplicationEvent remote acknowledgement event (otherwise it will be a dead loop, receiving messages and sending messages…) and if the event is sent by the application itself (the sender of the event is the application itself), if both are satisfied, execute step 3
- Construct the Message and use the remote event as a payload, then send the message to the broker using a MessageChannel with the Binding name springCloudBusOutput constructed by Spring Cloud Stream.
- The @StreamListener annotation consumes a MessageChannel with the Binding name springCloudBusInput constructed by Spring Cloud Stream, and the received message is a remote message.
- If the remote event is an AckRemoteApplicationEvent remote acknowledgement event and the application has enabled the message trace switch, and the remote event is not sent by the application itself (the event sender is not the application itself, which means that the event was sent by another application), then the locally sent AckRemoteApplicationEvent The remote acknowledgement event indicates that the application acknowledges the receipt of the remote event sent by another application. End of process
- If the remote event is sent from another application to the application itself (the recipient of the event is the application itself), then proceed to steps 7 and 8, otherwise perform step 9
- If the remote event is not sent by the application itself (the sender of the event is not the application itself), the event is sent out locally. The application itself has already been handled locally by the corresponding message recipient in the first place, so there is no need to send it again
- If the AckRemoteApplicationEvent remote acknowledgement event switch is enabled, construct the AckRemoteApplicationEvent event and send it both remotely and locally (locally because step 5 does not perform local AckRemoteApplicationEvent event is sent locally because step 5 did not perform local AckRemoteApplicationEvent event sending, that is, your own application confirms to your own application; it is sent remotely to tell other applications that your application received the message)
- If the message logging trace switch is turned on, the SentApplicationEvent event is constructed and sent locally
The EnvironmentChangeListener
of all nodes that listen for configuration changes after the bus-env
trigger will print the following message on the console
|
|
If the remote acknowledgement event AckRemoteApplicationEvent
is listened to locally, all nodes will receive the information, for example, the console of the node5
node listens to the AckRemoteApplicationEvent
event as follows.
|
|
To answer the four questions mentioned at the beginning of this section.
- How the message is sent: Send an event to the springCloudBus topic via Spring Cloud Stream in the BusAutoConfiguration#acceptLocal method
- How the messages are received: The springCloudBus topic is received in the BusAutoConfiguration#acceptRemote method via Spring Cloud Stream
- How destination is matched: The destination is matched in the BusAutoConfiguration#acceptRemote method in the ReceiveRemote event method
- How to trigger the next action after the remote event is received: The Bus internally receives the local RemoteApplicationEvent specific implementation event through Spring’s event mechanism and then does the next action (e.g. EnvironmentChangeListener receives the EnvironmentChangeRemoteApplicationEvent event, RefreshListener receives the RefreshRemoteApplicationEvent event)
Summary
The spring Cloud Bus itself is still relatively small, but you need to understand the Spring Cloud Stream system and Spring’s own event mechanism in advance, and then understand the logic of Spring Cloud Bus for handling local and remote events on this basis.
We can inherit RemoteApplicationEvent and build our own microservice messaging system with @RemoteApplicationEventScan annotation.
Reference https://fangjian0423.github.io/2019/04/09/spring-cloud-bus-intro/