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.
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
hangzhou is missing from the configuration of all nodes.
Bus internally provides
EnvironmentBusEndpoint which is an
Endpoint used to add/update configuration via
Visit the url
/actuator/bus-env?name=hangzhou&value=alibaba of any node to add a configuration item (e.g. visit the
Then visit all nodes again
/bus/env to get the configuration.
You can see that all nodes have a new configuration with
hangzhou and the corresponding
alibaba. This configuration item is done through the
EnvironmentBusEndpoint provided by
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
node1 (node2 is configured with
rocketmq-bus-node2:10002, which matches), make the following configuration changes.
/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
node2 have changed their configurations, while the remaining 3 nodes remain unchanged.
In-depth knowledge of bus
Bus Concept Introduction
The remote event
RemoteApplicationEvent is defined in Bus, which inherits from the
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 refreshing
UnknownRemoteApplicationEvent: 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
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
rebind operation to add/update the configuration.
Bus exposes 2
RefreshBusEndpoint, for adding/modifying configuration and global configuration refreshing. Their corresponding
Endpoint id i.e.
Bus for message delivery must involve
Group and so on. These are encapsulated in
BusProperties, whose default configuration prefix is
spring.cloud.bus.refresh.enabledis used to enable/disable the Listener for global refresh.
spring.cloud.bus.env.enabledEnable/disable Endpoint for configuration addition/modification.
spring.cloud.bus.ack.enabledEnable/disable sending of -
spring.cloud.bus.trace.enabledis 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
BusAutoConfiguration automation configuration class is modified by
The usage of
@EnableBinding is explained in the dry run｜Introduction to the
Spring Cloud Stream system and principles, and its
SpringCloudBusClient.class, which creates a DirectChannel for input and output based on the proxy in
SpringCloudBusClient. creates a
output based on the proxy.
The properties of the
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
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)
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.