Reactive Java Microservices with Spring Boot and JHipster
Java has been at the forefront of microservice architectures since they came to prominence a few years ago. It’s a popular language with well-known, high-quality frameworks, like Spring Boot, Spring Cloud, Spring Data, and Spring Security.
Spring Boot 2.0 introduced a new web framework called Spring WebFlux. Previous versions of Spring Boot only shipped with Spring MVC as an option. WebFlux offers a way for developers to do reactive programming. This means you can write your code with familiar syntax and, as a result, your app will use fewer resources and scale better.
- Why Reactive Java?
- Build a Reactive Java Microservices Architecture
- Define Your Reactive Java Architecture with JDL
- Run Your Reactive Java Microservices
- Test Your Reactive Java Microservices
- Prepare Your Reactive Java Stack for Production
- Create Docker Images for Your Microservice Apps
- Run Your Microservices Stack with Docker Compose
- What About Kotlin Microservices?
- How Do I Deploy to the Cloud?
- Should You Go Reactive?
- Learn More About Reactive Java and Microservices
You can also watch this tutorial as a screencast.
Why Reactive Java?
Reactive programming isn’t for every app. The general rule of thumb is it won’t help you if you have < 500 requests/second. Chances are Spring MVC will perform as well as Spring WebFlux up to that point. When your traffic takes off, or if you need to process things faster than 500 requests/second, you should take a look at Spring WebFlux.
Spring WebFlux’s API has a similar syntax to Spring MVC. For example, here’s the Spring MVC code for creating a new Points
entity.
/**
* {@code POST /points} : Create a new points.
*
* @param points the points to create.
* @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new points, or with status {@code 400 (Bad Request)} if the points has already an ID.
* @throws URISyntaxException if the Location URI syntax is incorrect.
*/
@PostMapping("/points")
public ResponseEntity<Points> createPoints(@Valid @RequestBody Points points) throws URISyntaxException {
log.debug("REST request to save Points : {}", points);
if (points.getId() != null) {
throw new BadRequestAlertException("A new points cannot already have an ID", ENTITY_NAME, "idexists");
}
Points result = pointsRepository.save(points);
pointsSearchRepository.save(result);
return ResponseEntity
.created(new URI("/api/points/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString()))
.body(result);
}
The same functionality when implemented with Spring WebFlux returns a Mono
and uses a more functional, streaming style to avoid blocking.
/**
* {@code POST /points} : Create a new points.
*
* @param points the points to create.
* @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new points, or with status {@code 400 (Bad Request)} if the points has already an ID.
* @throws URISyntaxException if the Location URI syntax is incorrect.
*/
@PostMapping("/points")
public Mono<ResponseEntity<Points>> createPoints(@Valid @RequestBody Points points) throws URISyntaxException {
log.debug("REST request to save Points : {}", points);
if (points.getId() != null) {
throw new BadRequestAlertException("A new points cannot already have an ID", ENTITY_NAME, "idexists");
}
return pointsRepository
.save(points)
.flatMap(pointsSearchRepository::save)
.map(
result -> {
try {
return ResponseEntity
.created(new URI("/api/points/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString()))
.body(result);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
);
}
In this guide, I’ll show you how to create a reactive microservices architecture with Spring Boot, Spring Cloud Gateway, Spring Cloud Config, Spring WebFlux, Java, and JHipster.
Prerequisites
You can find the completed source code for this example on GitHub, in the oktadeveloper/java-microservices-examples repository.
git clone https://github.com/oktadev/java-microservices-examples.git
cd java-microservices-examples/reactive-jhipster
Build a Reactive Java Microservices Architecture
JHipster is an open-source project that started as an application generator, based on Yeoman. It’s also the most popular Yeoman generator. JHipster initially allowed you to generate AngularJS and Spring apps. In the last five years, it’s become a platform with extensibility, grown a thriving community, and has saved developers many hours of pain and frustration.
You see, JHipster integrates everything for you. Do you want to use Angular with Spring Boot? It’s got that. React? Yep. Microservices with Spring Cloud? Check!
JHipster 7 was recently released, and it packs a punch! A couple of slick features that I like are Vue support and reactive microservices with Spring WebFlux. To be fair, WebFlux and Spring Cloud Gateway have been an option since JHipster 6.8.0 in March 2020, but now we have R2DBC support too!
In this tutorial, I’ll show you how to generate a microservice architecture that uses OAuth 2.0, an API gateway, and two microservices (a blog and a store). The gateway will use PostgreSQL with R2DBC, the blog will use Neo4j, and the store will use MongoDB. All persistence options are powered by Spring Data.
Start by installing JHipster using npm:
npm i -g generator-jhipster@7.0.1
After installing JHipster, you can run the following command to answer a whole slew of questions and create an app.
jhipster
However, there’s an easier way. JHipster has its own domain language!
Define Your Reactive Java Architecture with JDL
JHipster Domain Language (JDL) offers a way to define apps, so you don’t have to worry about fat-fingering your answer to the jhipster
command’s questions.
You can also generate your JHipster apps using JHipster Online. However, it’s a bit tedious when creating a microservice architecture. |
Create a new directory on your machine and navigate into it in your terminal.
take reactive-stack # mkdir reactive-stack && cd reactive-stack
git init # initialize git, so apps aren't created with their own .git
Copy the JDL below and put it into a reactive-ms.jdl
file.
application { config { baseName gateway (1) reactive true (2) packageName com.okta.developer.gateway applicationType gateway authenticationType oauth2 (3) buildTool gradle (4) clientFramework vue (5) prodDatabaseType postgresql serviceDiscoveryType eureka testFrameworks [cypress] (6) } entities Blog, Post, Tag, Product } application { config { baseName blog reactive true packageName com.okta.developer.blog applicationType microservice (7) authenticationType oauth2 buildTool gradle databaseType neo4j devDatabaseType neo4j prodDatabaseType neo4j enableHibernateCache false serverPort 8081 serviceDiscoveryType eureka } entities Blog, Post, Tag } application { config { baseName store reactive true packageName com.okta.developer.store applicationType microservice authenticationType oauth2 buildTool gradle databaseType mongodb devDatabaseType mongodb prodDatabaseType mongodb enableHibernateCache false serverPort 8082 serviceDiscoveryType eureka } entities Product } entity Blog { name String required minlength(3) handle String required minlength(2) } entity Post { title String required content TextBlob required date Instant required } entity Tag { name String required minlength(2) } entity Product { title String required price BigDecimal required min(0) image ImageBlob } relationship ManyToOne { Blog{user(login)} to User Post{blog(name)} to Blog } relationship ManyToMany { Post{tag(name)} to Tag{post} } paginate Post, Tag with infinite-scroll paginate Product with pagination microservice Product with store microservice Blog, Post, Tag with blog deployment { (8) deploymentType docker-compose appsFolders [gateway, blog, store] dockerRepositoryName "mraible" }
1 | The first app is an API gateway. |
2 | Because the gateway is reactive, it’ll use Spring Cloud Gateway. |
3 | The gateway and microservice apps must use the same authentication type. |
4 | Use Gradle, because a lot of y’all love it. |
5 | Vue support is new in JHipster 7, let’s use it! |
6 | JHipster 7 supports Cypress! It seems to be more reliable than Protractor. |
7 | Make sure and specify microservice as the application type for the blog and store apps. |
8 | JDL allows you to create Docker Compose and Kubernetes deployments too! |
See application configuration options to see the possible values for the above configuration options. |
Import this architecture definition and generate gateway
, blog
, and store
apps.
jhipster jdl reactive-ms.jdl
As part of this process, several Docker Compose files are generated for you. These allow you to run databases, the JHipster Registry (for service discovery), Keycloak (for identity), all with Docker.
Run Your Reactive Java Microservices
After JHipster finishes generating your apps, you can run them with Gradle. Assuming you’re in the same top-level directories you ran jhipster jdl
from, you can run the following commands to start all the backend services for each microservice.
JHipster has a Oh My ZSH! plugin that I highly recommend. It provides aliases for starting Docker containers and is a real time-saver. I’ve included these commands as comments below. |
cd gateway
docker-compose -f src/main/docker/keycloak.yml up -d #jhkeycloakup
docker-compose -f src/main/docker/postgresql.yml up -d #jhpostgresqlup
docker-compose -f src/main/docker/jhipster-registry.yml up -d #jhregistryup
./gradlew
You can run docker-compose -f src/main/docker/jhipster-registry.yml logs --follow to watch the logs of the JHipster Registry (or jhregistrylogs if you’re using Oh My Zsh and have the JHipster plugin installed).
|
The JHipster Registry is a Netflix Eureka server that handles service discovery. When the gateway and microservices start up, they register with Eureka. This allows communication between services using logical names, rather than IP address or host names. JHipster Registry also contains a Spring Cloud Config server that can distribute configuration between apps. You can learn more about Spring Cloud Config in Spring Cloud Config for Shared Microservice Configuration.
Open a new terminal window, start the blog app’s Neo4j database, and then the app itself.
cd ../blog
docker-compose -f src/main/docker/neo4j.yml up -d #jhneo4jup
./gradlew
Then, open another terminal window, start the store app’s MongoDB database, and the microservice.
cd ../store
docker-compose -f src/main/docker/mongodb.yml up -d #jhmongoup
./gradlew
To make Keycloak work, you need to add the following line to your hosts file ( 127.0.0.1 keycloak This is because you will access your application with a browser on your machine (which is named localhost, or |
Test Your Reactive Java Microservices
Open http://localhost:8080
in your favorite browser. You should be able to login with admin/admin
as credentials.
Make sure you can add a new blog, edit existing posts, and add new products.
To prove everything works in an automated fashion, you can run npm run e2e
in the gateway project’s directory. This will run a number of end-to-end tests with Cypress.
Prepare Your Reactive Java Stack for Production
Keycloak is a superb open source identity provider. It has excellent support for OAuth 2.0 and OpenID Connect (OIDC) and easily runs in a Docker container. I greatly appreciate Keycloak’s ease-of-use. I also appreciate Spring Security’s OAuth and OIDC support.
Spring Security makes it so you only need to override three properties to switch from Keycloak to Okta!
In production, you might not want to manage your own identity provider instance. That’s where Okta comes in. We’re a developer-friendly SaaS company that provides OAuth and OIDC support as a service.
Before you begin, you’ll need a free Okta developer account. Install the Okta CLI and run okta register
to sign up for a new account. If you already have an account, run okta login
.
Then, run okta apps create jhipster
. Select the default app name, or change it as you see fit.
Accept the default Redirect URI values provided for you.
What does the Okta CLI do?
The Okta CLI streamlines configuring a JHipster app and does several things for you:
- Creates an OIDC app with the correct redirect URIs:
- login:
http://localhost:8080/login/oauth2/code/oidc
andhttp://localhost:8761/login/oauth2/code/oidc
- logout:
http://localhost:8080
andhttp://localhost:8761
- login:
- Creates
ROLE_ADMIN
andROLE_USER
groups that JHipster expects - Adds your current user to the
ROLE_ADMIN
andROLE_USER
groups - Creates a
groups
claim in your default authorization server and adds the user’s groups to it
NOTE: The http://localhost:8761*
redirect URIs are for the JHipster Registry, which is often used when creating microservices with JHipster. The Okta CLI adds these by default.
You will see output like the following when it’s finished:
Okta application configuration has been written to: /path/to/app/.okta.env
Run cat .okta.env
(or type .okta.env
on Windows) to see the issuer and credentials for your app. It will look like this (except the placeholder values will be populated):
export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI="https://{yourOktaDomain}/oauth2/default"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID="{clientId}"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET="{clientSecret}"
NOTE: You can also use the Okta Admin Console to create your app. See Create a JHipster App on Okta for more information.
Update the JHipster Registry to Distribute OIDC Configuration
I mentioned earlier that Spring Cloud Config allows you to distribute Spring’s configuration between apps. In this section, you’ll configure JHipster’s Spring Security settings to use Okta across all your services.
Add the following YAML to gateway/src/main/docker/central-server-config/localhost-config/application.yml
. You can find the values for each property in the .okta.env
file.
spring:
security:
oauth2:
client:
provider:
oidc:
issuer-uri: https://<your-okta-domain>/oauth2/default
registration:
oidc:
client-id: <client-id>
client-secret: <client-secret>
Save your changes. These values will be distributed to the JHipster Registry, gateway, blog, and store apps. Restart the JHipster Registry by running the following commands:
docker-compose -f src/main/docker/jhipster-registry.yml down #jhregistrydown
docker-compose -f src/main/docker/jhipster-registry.yml up -d #jhregistryup
Use Ctrl+C to kill all your ./gradlew
processes and start them again.
Now, open a new incognito browser window, go to http://localhost:8080
, and sign in. Rejoice that using Okta for authentication works!
If you’re feeling lucky, you can set your Okta credentials as environment variables and run end-to-end tests (from the gateway
directory).
export CYPRESS_E2E_USERNAME=<your-username>
export CYPRESS_E2E_PASSWORD=<your-password>
npm run e2e
Create Docker Images for Your Microservice Apps
The JDL you used to create this reactive stack contains Docker configuration, so you can run everything with Docker Compose.
Stop all your apps with Ctrl+C. Stop all your Docker instances too.
docker stop $(docker ps -a -q)
Bump up the memory and CPU that Docker uses in Docker > Preferences > Resources. I have my Docker preferences set to 6 CPUs and 12GB of RAM. |
To run your reactive stack with Docker Compose, you need to create Docker images for each app. In your three different app directories, run the following Gradle command:
./gradlew -Pprod bootJar jibDockerBuild
Run Your Microservices Stack with Docker Compose
Once your Docker containers are finished building, you’ll want to add your Okta settings to Spring Cloud Config in JHipster Registry.
Switch Identity Providers with Spring Cloud Config
Open docker-compose/docker-compose.yml
in your favorite IDE (I like IntelliJ IDEA) and remove the Keycloak image at the bottom. You can leave it if you like, but it won’t be used in this example.
Update docker-compose/central-server-config/application.yml
to contain your OIDC settings that you want to share with all your microservices.
spring:
security:
oauth2:
client:
provider:
oidc:
issuer-uri: https://<your-okta-domain>/oauth2/default
registration:
oidc:
client-id: <client-id>
client-secret: <client-secret>
Prove Your Reactive Java Stack Works
Before you start everything with Docker, make sure you have adequate resources configured. The default is 2GB of memory and at least 6GB is recommended. Go to Docker Desktop > Preferences > Resources to configure. You can see my settings in the screenshot below.
In the docker-compose
directory, run the following command to start all your containers.
docker-compose up
You can add a -d to the above command to run it as a daemon. I like watching all the log messages dance with each other.
|
You should be able to open http://localhost:8080
, sign in, and access all of your microservices. Pretty slick, eh?! 🤓
What About Kotlin Microservices?
JHipster supports Kotlin-based microservices thanks to its Kotlin blueprint, supported by Sendil Kumar N.
You can install it using npm:
npm install -g generator-jhipster-kotlin
Then, use khipster jdl reactive-ms
to create the same stack you did above with Kotlin.
At the time of this writing, JHipster’s Kotlin blueprint doesn’t support JHipster 7. Watch the project’s releases page for updates. |
How Do I Deploy to the Cloud?
JHipster creates a cloud-native microservices architecture that can be deployed to many cloud providers. There’s specific support for AWS, Microsoft Azure, Heroku, and Google Cloud Platform.
However, if you’re doing microservices, you’ll probably want to leverage Docker as you did in this tutorial. When your apps are containerized, they can be orchestrated with Kubernetes.
JHipster has a Kubernetes sub-generator that you can use to deploy it to the cloud. I’ll cover this in a future tutorial.
In the meantime, you can watch a presentation that Ray Tsang and I did recently that shows how to deploy JHipster microservices with Kubernetes. If you start watching from 46:18, you’ll see Ray show how to deploy to Google Cloud using Kubernetes.
Should You Go Reactive?
As with most software architecture decisions, it depends. Are you building CRUD apps? Then no, Spring MVC is good enough.
Are you dealing with massive amounts of steaming data and millions of customers? Then yes, reactive frameworks like Spring WebFlux might just save you $$$ on your monthly cloud bill.
What about Project Loom? Will it allow you to write regular non-reactive code that performs as good as reactive frameworks? I’m not sure. I’m betting on reactive for now. I think it’s a good skill to have for Java developers.
If you want to learn more about Project Loom, I recommend listening to Episode 8 "Project Loom" with Ron Pressler from the Inside Java Podcast.
Learn More About Reactive Java and Microservices
This tutorial isn’t an in-depth guide to programming reactive Java microservices. That’s because it doesn’t have to be! With JHipster, you can generate high-quality reactive Java code (~70% test coverage) that’s based on fantastic frameworks like Spring Boot, Spring Cloud, Spring WebFlux, and Spring Security.
JHipster also implements most of the patterns in my Security Patterns for Microservice Architectures. You can add dependency scanning with Snyk (based on Brian Vermeer’s blog post), use HTTPS locally, adopt OAuth, add CI/CD, and generate secure Docker containers, just to name a few.
The Spring Cloud Gateway implementation in JHipster is largely based on what I learned when researching and writing Secure Reactive Microservices with Spring Cloud Gateway. Spring Cloud Gateway makes it trivial to relay an access token between a gateway and microservices. It’s just five lines of YAML:
spring:
cloud:
gateway:
default-filters:
- TokenRelay
You can find the completed source code for this example on GitHub, in the oktadev/java-microservices-examples repository.
git clone https://github.com/oktadev/java-microservices-examples.git
cd java-microservices-examples/reactive-jhipster
If you want to learn more about reactive programming’s nitty-gritty details, we have a few posts on this blog.
I’m proud to say that parts of this series were Josh Long’s initial drafts for his Reactive Spring book.
If you liked this post, you might like some of our other Java microservices posts:
We also have several tutorials that talk about JHipster specifically:
Keep in touch! If you have questions about this post, please ask them in the comments below. Follow @oktadev on Twitter, subscribe to our YouTube channel, and follow us on LinkedIn.
Changelog:
- May 12, 2021: Update org name to oktadev on GitHub and added recommended Docker preferences of 6GB of memory. See oktadev/okta-blog#791 for specific changes.
- Apr 28, 2021: Upgraded to JHipster 7.0.1. You can see the changes in the example app on GitHub. Changes to this article can be viewed in oktadev/okta-blog#779.
- Mar 25, 2021: Upgraded to JHipster 7.0.0 from beta 1. You can see the changes in the example app on GitHub. Changes to this article can be viewed in oktadev/okta-blog#627.
Okta Developer Blog Comment Policy
We welcome relevant and respectful comments. Off-topic comments may be removed.