# Spring Cloud Bus

In 
Published 2023-07-01

This tutorial explains how to build a Spring Cloud Bus. This tutorial contains an example as well.

# Spring Cloud Bus - Explanation

The main idea behind the Spring Cloud Bus is that when we are using Spring Config Server - Spring Config Clients, when a change is done in the configuration files, the change is not seen automatically by the Config Clients.

We can always install Spring Actuator on the client side and run http://<hostname>:<application-port>/actuator/refresh for refreshing the configuration of the service. This approach could work when we have a tiny number of services, but it is not working when we have a huge amount of services to manage. We need to find a solution to update these configurations automatically. This is the role of the Spring Cloud Bus.

The Spring Cloud Bus architecture works with a messaging system (Apache Kafka, RabbitMQ, etc). When the http://<hostname>:<application-port>/actuator/busrefresh POST command will run on any service, the config changes are send to the messaging system. The Config Clients are listening to the messaging system and when a change occurs, they will receive that change. In this case, one command (POST request) is enough for refreshing all the configurations for all the services.

Now let's implement the Spring Cloud Bus.

Starting from our 2 articles Configuration Server and Configuration Client we will need to do the following in addition:

# Install a messaging system (RabbitMQ in my case)

Installing a RabbitMQ server is quite easy. In my case, I have Docker Desktop installed on my laptop.

The following 2 commands will make you have RabbitMQ running on your machine:

docker pull rabbitmq:3-management
docker run -d --hostname my-rabbit --name some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management

We will configure RabbitMQ to use the default ports.

If you want to access the console, you can use the following link : http://localhost:15672/#/.

The default username is "guest" and the default password is "guest". RabbitMQ will also listen on port 5672.

# Configure the Config Clients

On the Config Clients we need to add the following dependencies:

pom.xml
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>3.1.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bus-amqp -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    <version>4.0.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-bus -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-bus</artifactId>
    <version>4.0.1</version>
</dependency>

In application.properties I have the following content:

server.port=8001
spring.application.name=client1-service
spring.config.import=optional:configserver:http://localhost:9900

# Actuator will show all the information which is not done by default
management.endpoint.health.show-details="ALWAYS"
management.security.enabled=false
management.endpoints.web.exposure.include=*

#For connecting to RabbitMQ
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

cloud.bus.enabled=true
cloud.bus.refresh.enabled=true

The Client controller looks like :

MyController.java
package com.example.configclient;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class MyController {

    @Value("${color.window}")
    String colorWindow;

    @Value("${color.car}")
    private String colorCar;

    @GetMapping("/readValues")
    public String status() {
        return  " <p> From the Config Server I have read: </p>" +
                " <p> color.window ="+ colorWindow + " </p>" +
                " <p> color.car ="+ colorCar + " </p>";
    }
}

Don't forget to add @RefreshScope !

# Configure the Config Server

On the Config Server we need to add the following dependencies:

pom.xml
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>3.1.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-monitor -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-monitor</artifactId>
    <version>4.0.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-stream-rabbit -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    <version>4.0.3</version>
</dependency>

In application.properties I have the following content:

server.port=9900
spring.application.name=spring-cloud-config-server
spring.cloud.config.server.git.uri =git@github.com:catatomoiu/config.git
spring.cloud.config.server.git.clone-on-start=true

# Actuator will show all the information which is not done by default
management.endpoint.health.show-details="ALWAYS"
management.security.enabled=false
management.endpoints.web.exposure.include=*

spring.cloud.inetutils.preferred-networks= 192.168.0

#RabbitMQ details
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

spring.cloud.inetutils.preferred-networks must be set when you have many network interfaces defined on your machine and InetUtils doesn't get the right hostname/localhost.

That's all. Not too much complicated.

# Test the Cloud Bus

Now, let's test the Spring Cloud Bus.

Without any modification, the Client will see the actual values from the configuration files.

We modify a config file:

We run http://localhost:8001/readValues in the browser again, but we will not see any changes.

Run curl -X POST http://localhost:9900/actuator/busrefresh (but this could be run on the client port as well) and when we run http://localhost:8001/readValues in the browser, we will see:

All works like a charm ! Enjoy !