Skip to content

Spring Cloud Bus in Depth | SpringBoot Microservices

The Airport PA Announcement Analogy

Imagine a busy international airport with seventy boarding gates. The airport authority needs to announce that Gate 40 through Gate 50 have had their boarding times delayed by twenty minutes. In an antiquated manual setup, a manager would have to physically walk to each of the eleven gate counters, tap the clerk on the shoulder, and repeat the instructions eleven times. If the manager forgets gate 47, passengers at that gate receive outdated information and miss their flight.

In a modern airport, the authority speaks once into a central microphone connected to the airport wide public address system. A single announcement broadcasts across the shared audio bus, and every speaker in every gate broadcasts the update simultaneously.

In microservices architecture, Spring Cloud Bus is that public address system. While @RefreshScope allows an individual microservice to reload its configuration without restarting, invoking /actuator/refresh on eighty separate instances in a cluster is impossible and error prone. Spring Cloud Bus connects all service instances together using a lightweight distributed message broker (such as RabbitMQ or Apache Kafka). Sending a single refresh request to any one instance broadcasts a refresh event across the bus, updating every microservice instance in the cluster in a single stroke.

This lecture covers the multi instance configuration challenge, Spring Cloud Bus architecture, integrating with RabbitMQ and Kafka, triggering broadcast refreshes via /actuator/busrefresh, and targeted routing with destination filters.


The Multi Instance Scaling Problem

Consider a production deployment where order-service is scaled to five instances:

[ Git Config Repository ] ---> [ Spring Cloud Config Server ]
                                      |
       +------------------------------+------------------------------+
       |                              |                              |
[ order-service:1 ]           [ order-service:2 ]           [ order-service:3 ]
(:8081)                       (:8082)                       (:8083)

Suppose an engineer modifies discount.rate in the Git repository.

Without Spring Cloud Bus:

  • To refresh the cluster, the engineer must issue three separate HTTP requests:
    • POST http://host1:8081/actuator/refresh
    • POST http://host2:8082/actuator/refresh
    • POST http://host3:8083/actuator/refresh
  • If you have fifty instances running on dynamic Kubernetes pods with ephemeral IPs, tracking down every pod IP is virtually impossible.
  • If one instance fails to receive the refresh call, your cluster enters an inconsistent split brain configuration state where some users receive the old discount and others receive the new discount.

How Spring Cloud Bus Solves Cluster Coordination

Spring Cloud Bus connects all microservice instances using a message broker:

                  [ Engineer updates Git ]
                             |
                  [ Config Server updated ]
                             |
                  (POST /actuator/busrefresh)
                             v
                 [ order-service: 1 (:8081) ]
                             |
              Publishes RefreshRemoteApplicationEvent
                             v
          +======================================+
          |     Message Broker (RabbitMQ / Kafka)|
          +======================================+
              |                              |
              v                              v
     [ order-service: 2 ]           [ order-service: 3 ]
     (:8082 - reloads config)       (:8083 - reloads config)

When you issue a POST request to /actuator/busrefresh on any single instance (or directly on the Config Server):

  1. That instance publishes a RefreshRemoteApplicationEvent onto the message broker topic.
  2. Every service instance subscribed to the bus receives the event.
  3. Each instance invokes its internal refresh mechanism, clearing its @RefreshScope caches and reloading properties.
  4. The entire cluster synchronizes within milliseconds.

Implementing Spring Cloud Bus with RabbitMQ

1. Add Dependencies in pom.xml

Add spring-cloud-starter-bus-amqp (AMQP is the protocol used by RabbitMQ):

xml
<dependencies>
    <!-- Spring Cloud Bus with RabbitMQ -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>

    <!-- Spring Boot Actuator to expose bus endpoints -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- Config Client & RefreshScope support -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>

(Note: For Apache Kafka, replace spring-cloud-starter-bus-amqp with spring-cloud-starter-bus-kafka).

2. Configure Connection in application.properties

properties
server.port=8081
spring.application.name=order-service

# RabbitMQ Broker Connection Details
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

# Expose busrefresh Actuator Endpoint
management.endpoints.web.exposure.include=health,info,busrefresh

# Spring Cloud Bus is enabled by default when the dependency is present
spring.cloud.bus.enabled=true

Triggering Cluster Wide Refreshes

Once instances are connected to RabbitMQ:

1. Broadcast Refresh to All Services

Send an HTTP POST request to any instance's /actuator/busrefresh endpoint:

bash
curl -X POST http://localhost:8081/actuator/busrefresh

Every service instance listening on the bus refreshes its configuration simultaneously.

2. Targeted Refresh Using Destination Filtering

Sometimes you do not want to refresh the entire cluster. You only want to refresh instances of order-service, without disturbing product-service or payment-service.

Spring Cloud Bus provides the destination parameter:

bash
# Refresh only order-service instances across the cluster
curl -X POST "http://localhost:8081/actuator/busrefresh?destination=order-service:**"

You can even target a specific instance by combining application name and port:

bash
# Refresh only the instance on port 8082
curl -X POST "http://localhost:8081/actuator/busrefresh?destination=order-service:8082"

The destination pattern format is appName:port:profile, supporting wildcards (* and **).


Automating Refreshes with Git Webhooks

You can automate the entire pipeline so that pushing a commit to GitHub or GitLab automatically updates all running microservices with zero manual intervention:

1. Engineer pushes commit to GitHub (e.g. discount.rate=0.25)
                     |
2. GitHub Webhook triggers POST to Config Server:
   POST http://config-server:8888/monitor
                     |
3. Config Server publishes RefreshRemoteApplicationEvent to RabbitMQ
                     |
4. All running microservices receive event and reload configuration

This delivers true continuous configuration delivery: change a property in Git, push the commit, and every instance in your cluster updates live within seconds.


Interview Questions & Pitfalls

Q1: What is the key difference between /actuator/refresh and /actuator/busrefresh?

/actuator/refresh reloads configuration only on the single microservice instance that received the HTTP request. /actuator/busrefresh publishes a refresh event to a shared message broker (such as RabbitMQ or Kafka), broadcasting the refresh signal to every connected service instance in the cluster.

Q2: Which message brokers are supported by Spring Cloud Bus?

Spring Cloud Bus officially supports RabbitMQ (via spring-cloud-starter-bus-amqp) and Apache Kafka (via spring-cloud-starter-bus-kafka).

Q3: How do you refresh only a specific microservice rather than the entire cluster when using Spring Cloud Bus?

Use the destination query parameter with /actuator/busrefresh. For example: POST /actuator/busrefresh?destination=order-service:** targets only instances whose application name matches order-service, leaving all other services untouched.

Q4: What happens if a microservice is offline when a RefreshRemoteApplicationEvent is published to the bus?

If an instance is offline, it misses the transient event. However, this is not a problem: when the offline instance starts back up, its standard bootstrap lifecycle queries the Config Server directly, loading the latest configuration from Git during startup.

Q5: How can configuration reloads be automated upon Git commits?

By configuring a Git webhook on your repository (GitHub, GitLab) that sends an HTTP POST request to the Spring Cloud Config Server's /monitor endpoint upon commit. The Config Server detects the changed files and automatically broadcasts a refresh event across the Spring Cloud Bus.