When we start to build something based on Microservice design, we’d inevitably need a distributed configuration for our applications. Having a distributed configuration makes scaling them out a lot easier. This post shows how to configure Micronaut to pick up a distributed configuration from Consul. All these in a local development environment.
Requisites
These are the following items we used in this post.
- JDK 14 for 64-bit for Windows 10
- Micronaut 2.4.1
- IntelliJ IDEA 2020.3 – Optional
- Docker for Windows
- Consul Docker image
Create a Micronaut Application In IntelliJ IDEA
First, let’s create a Micronaut Application in IntelliJ, as shown below. Go to File > New > Project..., choose Micronaut, and click Next.
Then, fill in the Group, Artifact, and Application type. Choose Application (A Micronaut Application).
Finally, include Consul Distributed Configuration. This enables our Micronaut application to consume distributed configuration from Consul. Click Next to create the Micronaut project.
Our Micronaut project will have the following project structure and files.
Now, we have a template project to work on.
Modify Our Micronaut Application
First, modify bootstrap.yml, as shown below. We added the last two lines.
1 2 3 4 5 6 7 8 | micronaut: application: name: distributedconfig config-client: enabled: true consul.client.defaultZone: ${CONSUL_HOST:localhost}:${CONSUL_PORT:8500} consul.client.registration.enabled: true consul.client.config.enabled: true |
Next, create a Web controller that’ll greet the user when he visits a specific URI. For example, HTTP://localhost:8080/hello. Within this controller, we’ll display the value of a property named greeting.msg, which we’ll define in Consul.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.turreta.micronaut.distributedconfig; import io.micronaut.context.annotation.Value; import io.micronaut.http.MediaType; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; @Controller("/hello") public class HelloController { @Value("${greeting.msg}") String greetingMsg; @Get(produces = MediaType.TEXT_PLAIN) public String index() { return greetingMsg; } } |
Don’t start up our Micronaut application yet, because we still don’t have a Consul instance running.
Startup a Consul Docker Container For Our Distributed Configuration
Next, let’s start up a Consult Docker container using the following command.
1 | docker run -p 8500:8500 consul |
Define Distributed Configuration In Consul For Our Micronaut Application To Use
Then, go to the Consul dashboard via http://localhost:8500/.
We can now define a value for the greeting.msg property in Consul. Go to the Key/Value tab and create the folder config and its sub-folder application.
Finally, create the greeting.msg property.
Startup Our Micronaut Application To Consume Distributed Configuration From Consul
Now, we can start up our Micronaut application to consume a distributed configuration from Consul.
Then, access the URL HTTP://localhost:8080/hello.
Why did we create those folders in the first place? The /config folder is the default path where Micronaut (not Consul) looks for key/pair values for properties like greeting.msg. However, the search path is configurable via the consul.client.config.path property. Meaning, if we modified our bootstrap.yml file as follows, we’d get an error when accessing the same URI.
1 2 3 4 5 6 7 8 9 | micronaut: application: name: distributedconfig config-client: enabled: true consul.client.defaultZone: ${CONSUL_HOST:localhost}:${CONSUL_PORT:8500} consul.client.registration.enabled: true consul.client.config.enabled: true consul.client.config.path: /otherpath |
We’d need to define the same property in /otherpath/application. How about the application/ folder? It is one of the sub-folders Micronaut looks for configuration in Consul.
Configuration Resolution Precedence
Directory | Description |
---|---|
/application | Configuration shared by all applications |
/application,prod | Configuration shared by all applications for the prod Environment |
/[APPLICATION_NAME] | Application-specific configuration, example /config/hello-world |
/[APPLICATION_NAME],prod | Application-specific configuration for an active Environment |
The APPLICATION_NAME property refers to the name of the application we defined in the bootstrap.yml (see micronaut.application.name). Meaning, for our Micronaut application, we can define the same property under the /config/distributeconfig path, and the property greeting.msg will be resolved if it hasn’t been defined elsewhere.
The Micronaut starter service (https://launch.micronaut.io) appears to create the
file when we include the Consul Distributed Configuration feature. We tried creating a Micronaut application without it and an