Learn Microservices Book: Spring Boot 2.4 Upgrade Guide
Step-by-step migration guide from Spring Boot 2.3 to Spring Boot 2.4 for the Learn Microservices with Spring Boot book's microservice architecture project
Step-by-step migration guide from Spring Boot 2.3 to Spring Boot 2.4 for the Learn Microservices with Spring Boot book's microservice architecture project
Complete 4-part guide series
Step-by-step migration guide from Spring Boot 2.3 to Spring Boot 2.4 for the Learn Microservices with Spring Boot book's microservice architecture project
An extra chapter showing how to update the book's source code from Spring Boot 2.4 to 2.5.5 and Java 17
The extra chapter for the book's source code update from Spring Boot 2.5 to 2.6 and from Spring Cloud 2020 to 2021.0
The extra chapter for the book's source code update from Spring Boot 2.6 to 2.7 with H2 database fixes and configuration improvements
This chapter describes how to upgrade the Learn Microservices with Spring Boot book’s source code to Spring Boot 2.4.0, Spring Cloud 2020.0, and Java 15.
Moving from Spring Boot 2.3.4 to 2.4.0 means we deal with a minor update (see the semantic versioning spec if you’re not familiar with the term). Why would I worry about writing a new post and publish the updated source code then?
About this guide: This is a practical approach to migrating a Spring Boot project from version 2.3 to version 2.4 and Spring Cloud 2020.0. It uses the codebase from the Learn Microservices with Spring Boot book’s practical example, but it’s applicable to many other projects too. If you want to dive into how Spring Boot works and set up a complete microservice architecture, get a copy now.
In the book, I invite the readers to create the microservices using the Spring Initializr at start.spring.io. If you select the latest version of Spring Boot available at the time of writing this article, 2.4.0, you’ll also get the latest version of Spring Cloud if you add any of its starters. You can try downloading the artifact through this link and checking the pom.xml file.
The latest version of Spring Cloud, 2020.0, is a major upgrade, which means it can break stuff. And it actually does. So does it a “minor change” introduced in Spring Boot 2.4.0 for embedded databases.
Let’s analyze and handle these changes one by one while we upgrade the source code of the book’s last chapter. All the code is available on GitHub (while you’re there, star it!).
First, we need to download and install the JDK version 15 in case we haven’t done that already.
Then, we update our poms. The code snippet in Listing 1 is an example of the changes we need to make, in this case for the Multiplication microservice. Check the highlighted lines.
spring-boot-starter-parent dependency to 2.4.0. Starting with this version, Spring Boot will not include the suffix RELEASE. Note that, since we didn’t include library versions in any of our dependencies because it’s managed by Spring Boot’s dependency management, we’ll get all the library updates that Spring Boot decided to include in this new version. Check this link if you want to see the complete list.java.version property is 15, which means we’ll compile using JDK 15. Make sure you set your JAVA_HOME environment variable accordingly.0.0.1-SNAPSHOT to 2.4.0-SNAPSHOT. This way, I can just follow the same versioning as Spring Boot, which will drive the further upgrades.<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>microservices.book</groupId>
<artifactId>multiplication</artifactId>
<version>2.4.0-SNAPSHOT</version>
<name>multiplication</name>
<description>Multiplication Microservice - Learn Microservices with Spring Boot - Upgrade 2.4.0</description>
<properties>
<java.version>15</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<!-- ... they remain the same, versions are updated because of the parent -->
</dependencies>
</project>
Listing 1. Upgrading Spring Boot and Java
Spring Boot 2.4 does bring a breaking change for the book’s projects. The default username is no longer sa, as covered in the book. What does this mean? If you already ran the projects using the Spring Boot 2.3.x version, the databases use sa as username and an empty password. Therefore, if you migrate to Spring Boot 2.4.0 and keep the same database files, you’ll get this error during the initialization of the Multiplication and the Gamification microservices:
org.h2.jdbc.JdbcSQLInvalidAuthorizationSpecException: Wrong user name or password [28000-200]
There are two ways we can fix this:
.db extension.sa using Spring Boot properties. See Listing 2.spring.application.name=multiplication
# Gives us access to the H2 database web console
spring.h2.console.enabled=true
# Creates the database in a file
spring.datasource.url=jdbc:h2:file:~/multiplication;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE;
spring.datasource.username=sa
Listing 2. Setting the database’s username to ‘sa’
You only need to do this in the Gamification and the Multiplication projects.
Source Code: The book’s source code with all the updates is available on GitHub: U.2.4.0. While you are there, please give it a star!
You can update all Spring Boot projects to the latest Spring Boot version: Multiplication, Gamification, Gateway, and Logs. However, this is only one part of the upgrade; the projects won’t work as they are at this point. You will get some errors if you try to run the apps now:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'configurationPropertiesBeans' defined in class path resource [org/springframework/
cloud/autoconfigure/ConfigurationPropertiesRebinderAutoConfiguration.class]
The Spring Cloud Hoxton release is not compatible with Spring Boot 2.4 (nor with JDK 15), so we need to continue with the upgrade and include Spring Cloud as well.
Anyway, we wanted to upgrade Spring Cloud, so let’s see what changes we need to apply to our projects.
Note: This post has been updated on Feb 13th, 2021 to use the Spring Cloud release version, instead of the milestone.
To include the 2020.0 (Ilford) version of Spring Cloud, we need to update all our poms as shown in Listing 3.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- ... -->
<properties>
<java.version>15</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
</project>
Listing 3. Upgrading Spring Cloud
In principle, we only need to change the Spring Cloud version property in all the pom.xml files for the projects using Spring Cloud, which are all the microservices - Multiplication, Gamification, Gateway, and Logs.
Let’s go through the needed changes to make our projects work after upgrading to this new Spring Cloud version.
When we upgrade to Spring Cloud 2020.0, we lose the bootstrap functionality because it has been removed by default. In our projects, we use the bootstrap phase with its corresponding bootstrap.properties file to adjust the settings of the Consul’s Config Server. This means our services will fail when running them in Docker containers because they can’t find the docker profile configuration we created to initialize the RabbitMQ host address. Without loading this profile, the microservices will try to reach the RabbitMQ server locally (from localhost - the container’s host).
To fix this, we have several options as documented in the release notes. In my case, I chose to add an extra dependency to all projects. See Listing 4 for the new dependency we can add to all projects using Spring Cloud to enable Bootstrap again.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
Listing 4. Adding the Spring Cloud Starter Bootstrap
Alternatively, we could pass an environment variable to enable bootstrap, but this new dependency introduced with this Spring Cloud version makes it more transparent in my opinion.
As the Github user mi2shiii found out, Spring Cloud Consul Config doesn’t use anymore the spring.application.name to search for profile combinations after the Spring Cloud 2020.0 upgrade. It tries to find matches like application,rabbitmq-production, and we want it to be multiplication,rabbitmq-production. Check the Github issue to see the details in the logs.
The reason is that, in the book, I included the application name property in the application.properties file. With the change introduced by Spring to the bootstrap phase, this value doesn’t get loaded prior to looking for profiles. There used to be a logic to merge values from both property sources that is no longer there.
The fix is simple. Just move the spring.application.name properties from application.properties to bootstrap.properties. This is a good practice anyway since there are processes in the bootstrap phase that require or may use the application name.
See the changes in the Pull Request.
In the new release of Spring Cloud Sleuth, the legacy MDC keys such as X-B3-TraceId and X-B3-SpanId have been renamed. Now, these keys are aligned with the ones that Brave (the underlying tracing engine) uses natively.
The logs microservice refers to these traces to print a formatted message. It’s included in the Logback’s logging pattern in the logback-spring.xml file:
<Pattern>
[%-15marker] [%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}] %highlight(%-5level) %msg%n
</Pattern>
If we keep those values after upgrading Spring Cloud, this service will print empty strings as trace identifiers in the logs.
At a first glance, this issue looks very easy to fix. We modify the MDC key names to match the Brave’s values traceId and spanId:
<Pattern>
[%-15marker] [%X{traceId:-},%X{spanId:-}] %highlight(%-5level) %msg%n
</Pattern>
However, if we follow that approach, we’ll observe that the identifiers don’t match between the services, and there isn’t any more a unique traceId for the complete process (credits to the reader mi2shiii for reporting the error). This is probably caused by a change in how the Spring’s AmqpAppender for logging purposes work. In the book, I was taking advantage of the fact that the logging appender used the same trace identifier as the rest of the process. However, that’s a bit fragile, as we just saw.
Therefore, instead of trying to make the traces match again, I created an alternative solution. Now, we embed the trace identifiers in the logged messages from the services (see Listing 6), which makes the centralized logger simpler since it just needs to print the message.
<appender name="AMQP"
class="org.springframework.amqp.rabbit.logback.AmqpAppender">
<layout>
<pattern>%d{HH:mm:ss.SSS} [trace id: %X{traceId:-}, span id: %X{spanId:-}] [%t] %logger{36} - %msg</pattern>
</layout>
</appender>
Listing 6. Changing the logging pattern, so it contains the trace and span IDs
See the complete changes on Github’s Pull Request #4.
We can also upgrade the Consul version used for the Consul configuration importer and the service registry. We’ll set it to version 1.9. See Listing 6, where the base image is now updated to consul:1.9. To avoid confusion, we can tag the Docker image we generate with this definition as consul-importer:1.9.
FROM consul:1.9
COPY ./consul-kv-docker.json /usr/src/consul/
WORKDIR /usr/src/consul
ENV CONSUL_HTTP_ADDR=consul:8500
ENTRYPOINT until consul kv import @consul-kv-docker.json; do echo "Waiting for Consul"; sleep 2; done
Listing 6. Upgrading the base image of the consul importer’s Dockerfile (docker/consul folder).
The main Consul container can also use this Docker image. See Listing 7 for a fragment of the updated docker-compose.xml file.
version: "3"
services:
# ...
consul-importer:
image: consul-importer:1.9
# ...
consul-dev:
image: consul:1.9
# ...
rabbitmq-dev:
image: rabbitmq:3-management
# ...
Listing 7. Docker Compose file with upgraded versions
Now, the Docker Compose file pulls the consul-importer:1.9 image (built and tagged by us with the Dockerfile shown in Listing 5), and the consul:1.9 image available in the public Docker registry.
Note that a newer RabbitMQ image will be pulled automatically since we used a tag that the Docker image’s maintainers move across versions (as long as the major version is 3). The 3-management tag moves over time to point to the latest 3.x.x version.
I made all the new microservice versions available from the public registry. Use the docker-compose-public.yml file to pull the new images and run the complete system:
docker $ docker-compose -f docker-compose-public.yml up
Source Code: The book’s source code with all the updates is available on GitHub: U.2.4.0. While you are there, please give it a star!
Complete 4-part guide series
Step-by-step migration guide from Spring Boot 2.3 to Spring Boot 2.4 for the Learn Microservices with Spring Boot book's microservice architecture project
An extra chapter showing how to update the book's source code from Spring Boot 2.4 to 2.5.5 and Java 17
The extra chapter for the book's source code update from Spring Boot 2.5 to 2.6 and from Spring Cloud 2020 to 2021.0
The extra chapter for the book's source code update from Spring Boot 2.6 to 2.7 with H2 database fixes and configuration improvements
ENJOYED THIS ARTICLE?
Join hundreds of software architects and engineers getting practical insights delivered to their inbox.