Refreshing configuration at Runtime with Spring Cloud Bus and Azure Service Bus

49% of correspondents said to be using Spring Cloud Config Server

Create Config Server and Config Client application

Let's start with the Config client application. We need a series of dependencies like Web, Actuator, Config Client, and Cloud Bus. I named the application as config-client.

Generate project with Spring Initializr
package io.jaylee.cloudbus.configclient;

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

@SpringBootApplication
public class ConfigClientApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}

}

@RestController
@RefreshScope
class HelloRestController {
@Value("${message}")
String message;

@GetMapping("/hello")
public String sayHello() {
return message;
}
}
Setting up repository URI for configuration.
$ az spring-cloud app create -n cloud-bus-app -s asc-standard -g asc-rg --runtime-version=Java_17 --assign-endpoint true
This command usually takes minutes to run. Add '--verbose' parameter if needed.
[1/3] Creating app cloud-bus-app
[2/3] Creating default deployment with name "default"
[3/3] Updating app "cloud-bus-app" (this operation can take a while to complete)
App create succeeded
......
$ pwd
/Users/jaylee/Desktop/config-client
$ ls
HELP.md mvnw mvnw.cmd pom.xml src
$ az spring-cloud app deploy -n cloud-bus-app -s asc-standard -g asc-rg --source-path .
This command usually takes minutes to run. Add '--verbose' parameter if needed.
[1/3] Requesting for upload URL.
[2/3] Uploading package to blob.
[3/3] Updating deployment in app "cloud-bus-app" (this operation can take a while to complete)
Trying to fetch build logs
......
$ az spring-cloud app show -n cloud-bus-app -s asc-standard -g asc-rg --query properties.url
"https://asc-standard-cloud-bus-app.azuremicroservices.io"
$ curl https://asc-standard-cloud-bus-app.azuremicroservices.io/hello
Config from application.yml on Github
Showing the origin of each property

Refreshing configuration at runtime with Spring Cloud Bus and Azure Service Bus

Before I go into the details of Spring Cloud Bus, I'd like to revisit how refresh works with Spring Cloud Config. Any configuration changes in the Git repository with Spring Cloud Config don't trigger the refresh of configuration on apps at runtime. To refresh the configuration at runtime, you need to trigger the endpoint(/actuator/refresh) manually for every single app. For example, if you have 100 apps running, you should POST 100 times to different endpoints. That would not be a trivial job, especially if you're running it on Kubernetes with an additional internal management port for Actuator. Spring Cloud Bus can be a lifesaver that can propagate the refresh event to all the running applications using a message queue, eliminating the manual refresh job. Here is the introduction from the official Spring documentation.

Spring Cloud Bus, 3. POST endpoint is wrong, but the entire flow is valid
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId>
<version>4.1.0</version>
</dependency>
$ az servicebus namespace create --resource-group sandbox-rg --name cloudbus-test --location westus --sku Standard
...
$ az servicebus topic create --resource-group sandbox-rg --namespace-name cloudbus-test --name springcloudbus
...
$ az servicebus topic subscription create --resource-group sandbox-rg --namespace-name cloudbus-test --topic-name springcloudbus --name springcloudbus-subscription
...
spring:
application:
name: cloud-bus-app
cloud:
bus:
enabled: true
refresh:
enabled: true
trace:
enabled: true
azure:
servicebus:
connection-string: [REDACTED] #5
stream:
bindings:
springCloudBusInput: #1
destination: springcloudbus #2
group: springcloudbus-subscription #3
springCloudBusOutput:
destination: springcloudbus
servicebus:
bindings:
springCloudBusInput:
consumer:
auto-complete: false
springCloudBusOutput:
producer:
entity-type: topic #4

Test it on Azure Spring Apps

client-config is ready for deployment. The whole purpose of writing this article is to show you how to refresh the configuration of a fleet of applications, so surely we need more than one application. For that, I will create one more app as cloud-bus-app2.

$ pwd
/Users/jaylee/Desktop/servicebus/config-client
$ az spring-cloud app create -n cloud-bus-app2 -s asc-standard -g asc-rg --runtime-version=Java_17 --assign-endpoint true
...
$ az spring-cloud app deploy -n cloud-bus-app2 -s asc-standard -g asc-rg --source-path .
...
Tabs open for two URLs
$ curl -X POST https://asc-standard-cloud-bus-app.azuremicroservices.io/actuator/busrefresh
{
"type": "RefreshRemoteApplicationEvent",
"timestamp": 1653442315150,
"originService": "cloud-bus-app:1025:d58f42abdbad0832c1722be54ecfd0e3",
"destinationService": "**",
"id": "aec4dfc5-33da-4293-a9de-61c424f0230d"
}
{
"type": "AckRemoteApplicationEvent",
"timestamp": 1653442316258,
"originService": "cloud-bus-app2:1025:329dbed833014f492f699242fbef348a",
"destinationService": "**",
"id": "1778b410-2d55-4e5a-b6be-75aa09ccb966",
"ackId": "aec4dfc5-33da-4293-a9de-61c424f0230d",
"ackDestinationService": "**",
"event": "org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent"
}

Wrapping Up

Spring Cloud Bus and Spring Cloud Stream work hands-in-hand without much coding, giving excellent infrastructure to refresh the configuration at runtime without any downtime. Thanks to Azure Spring Apps, it doesn't take much effort to build and test it end to end.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jay Lee

Jay Lee

Cloud Native Enthusiast. Java, Golang, Spring, Kubernetes.