In the previous post we created basic Spring Web REST API for rental pricing.
In this post, we will transform it into Spring Cloud project and add the centralized configuration.
It means we will keep configuration for all our services in single place, easy for modification and all the changes will be discovered by services without the necessity of changing their code.
The project for this post is available at GitHub branch spring-cloud-2
Configuring Config Service
We need another application which will be our Config Service and Pricing Service will connect to it.
We can generate Config Service app with Spring Initializr at http://start.spring.io.
All we need is to choose "Config Server" and "Web" from dependencies.
This would generate app with maven dependencies in pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
Then we need to tell Spring Boot that this is a Config Service app with annotation on main class:
@EnableConfigServer
It will add some new REST mappings to the application which we can see in logs when the application starts.
Then add these properties in application.properties
server.port=8888
spring.cloud.config.server.git.uri=${HOME}/cloudconfigs
The last property tells Config Service where to look for services configurations, e.g. we can use local git repo, GitHub or bitbucket.
We will use local git repo. Let's create it. Execute commands in bash:
mkdir -p ${HOME}/cloudconfigs
cd ${HOME}/cloudconfigs
git init
Then create a new file pricing-service.yaml
in ${HOME}/cloudconfigs
and add these properties:
pricing:
plan:
basic: 5
standard: 200
vip: 500
Commit the file.
git add pricing-service.yaml
git commit -m 'added config'
Here we use different values than in the initial Pricing Service configuration to spot the difference.
These values will overwrite any local configuration in Pricing Service.
If we wanted, we could also add application.properties
or application.yaml
to the repo which would keep common properties for all services.
E.g. we could put
pricing:
plan:
basic: 1
but in this case, if we start Config Service and query for configuration for pricing-service the value will be overridden with setting from pricing-service.yaml
We can check this at http://localhost:8888/pricing-service/default
It would respond with JSON like:
{
"name":"pricing-service",
"profiles":[
"default"
],
"label":null,
"version":"8ab2211dd33751b797afa3d0a0eea8fccc10e676",
"state":null,
"propertySources":[
{
"name":"/home/marcin/tmp/cloudconfig/application.yaml",
"source":{
"pricing.plan.basic":5,
"pricing.plan.standard":200,
"pricing.plan.vip":500
}
}
]
}
Configuring config client
In Pricing Service we need to add a dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
and dependencyManagement
section:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
and version configuration in properties
:
<properties>
...
<spring-cloud.version>Finchley.RC2</spring-cloud.version>
</properties>
Then add a file resources/bootstrap.yaml
This is a property file which will be read as first when the application starts.
It is how Spring Cloud works. It first reads bootstrap.yaml
to determine additional configuration to be used.
In our cases, it will use the configuration from Config Service.
The file will look as follows:
spring:
application:
name: pricing-service
cloud:
config:
uri: http://localhost:8888
where spring.application.name
will be the name of the service telling Config Service which file to grab from config git repo and spring.cloud.config.uri
is a URL of Config Service.
By default Config Service starts on localhost:8888
.
Now if we start Pricing Service the order in which properties are resolved is the following:
-
pricing-service.properties
in config git repo -
application.yaml
in config git repo (if property not present inpricing-service.properties
) -
application.yaml
in pricing service (if property not present inapplication.yaml
in config git repo)
What would happen if the Config Service was down? The local properties from Pricing Service will be used.
Config Service has many nice features like properties per spring profile or label and properties encryption.
You can check a nice explanation here or of course in Spring Cloud docs
The final code for this part is available at GitHub spring-cloud-2 branch.
We have now centralized configuration for our services delivered by Config Service, but all services would need to have URL to the Config Service given at runtime (either in local application.yaml
or through environment variable).
This is a bit cumbersome if there are different URLs for integration, QA, PROD environments.
In the next post we will add service discovery mechanism so that services can use only a friendly name and the URL will be provided from the service registry.
Top comments (1)
Thanks for the tutorial. It's worth noting that the ${HOME} setting works for Unix machines only. On windows, I had to use ${USERPROFILE} which worked.
Again, thanks for the tutorial.