Search code examples
spring-bootreflectionannotationsspring-cloud-configspring-cloud-config-server

changing custom annotations on the fly from Spring Cloud Config Server. Is it possible?


Context: I need to provide a way to change parameter values during production on lower performance cost as possible.

Goal: I want change annotation values on fly and apply it at once on all microservices instances.

Personal background and limitations: I know I can use Spring Cloud Config to change parameters on the fly like explained in this article and I Know there is some challenges and pitfalls involved on changing annotations on the fly also like discussed in stackoveflow question.

I know that Spring Cloud Config can be used for setting up a centralized configuration applied to all microservice instances during boot/start. I have used it a bit. I am wondering if I can use it for centralizing parameters that can affect customized annotations on fly.

An imagined solution is:

... whenever I need somepRopertyValue

@Value("${config.somePropertyValue}")
private String somePropertyValue;

@Bean
public String somePropertyValue(){
    return somePropertyValue;
}

A config client in all microservices endpoint that must be call not only when the application start but whenever somePropertyValue managed in Spring Cloud Config Server bootstrap.properties is updated:

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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class SpringConfigClientApplication {

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

@RefreshScope
@RestController
class MessageRestController {

    @Value("${server.somePropertyValue:Unable to connect to config server}")
    private String somePropertyValue;

    @RequestMapping("/server/somePropertyValue")
    String getSomePropertyValue() {
        return this.somePropertyValue;
    }
}
 

And somehow somePropertyValue is maintened in Spring Cloud Config and if change during production time it affects on demand everywhere somePropertyValue is annoted in all microservice instances.

I am currently reaching this behaviour by adding a kafka consumer in all SpringBoot microservices that listen/observe a topic and when it receives a new messagge it changes on the fly the parameter value. It seems so odd that I created a Kafka dependency in all company microservices. Since I have used Spring Config for a bit similar scenario I am wondering if there is a better alternative using some out-of-box Spring approach. Also performance is highly important in my case and a bit delay on syncronize all parameters isn't an issue. By delay I mean that two or three seconds to update parameters in all microservices isn't an issue.


Solution

  • There are two ways to do that:

    i- There's a refresh endpoint, and you can actually call that for a service, and it'll actually refresh its configurations without restarting itself, which is pretty neat. e.g. MS-A is listing on 8080 then do a POST request at this endpoint: localhost:8080/refresh.

    NOTE: Spring Actuator actually adds a RefreshEndpoint to the app automatically when we annotate a controller in MS-A with @RefreshScope.

    ii- What you can also do is use Spring Cloud Bus, and broadcast an event, and then every service listens on that and refreshes itself. That's handy if you have dozens of services all using the Config Server, and you don't want to go one by one and hit a /refresh endpoint as we have did in 1st approach. You just want to broadcast a message to a bus and have all these things automatically pick it up.

    Reference: Both concepts I've learnt while taking course at Pluralsight