Reactive Java Microservices with Spring Boot and JHipster

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.

Spring Boot 2.0

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.

Reactive Java Microservices

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
JHipster prompts

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 (/etc/hosts on Mac/Linux, c:\Windows\System32\Drivers\etc\hosts on Windows).

127.0.0.1	keycloak

This is because you will access your application with a browser on your machine (which is named localhost, or 127.0.0.1), but inside Docker, it will run in its own container, which is named keycloak.

Test Your Reactive Java Microservices

Open http://localhost:8080 in your favorite browser. You should be able to login with admin/admin as credentials.

Keycloak login
Keycloak login success

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.

Protractor tests success

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:

  1. Creates an OIDC app with the correct redirect URIs:
    • login: http://localhost:8080/login/oauth2/code/oidc and http://localhost:8761/login/oauth2/code/oidc
    • logout: http://localhost:8080 and http://localhost:8761
  2. Creates ROLE_ADMIN and ROLE_USER groups that JHipster expects
  3. Adds your current user to the ROLE_ADMIN and ROLE_USER groups
  4. 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!

Okta login
Okta login success

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.

Docker Preferences

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.
JHipster colors

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:

Matt Raible is a well-known figure in the Java community and has been building web applications for most of his adult life. For over 20 years, he has helped developers learn and adopt open source frameworks and use them effectively. He's a web developer, Java Champion, and Developer Advocate at Okta. Matt has been a speaker at many conferences worldwide, including Devnexus, Devoxx Belgium, Devoxx France, Jfokus, and JavaOne. He is the author of The Angular Mini-Book, The JHipster Mini-Book, Spring Live, and contributed to Pro JSP. He is a frequent contributor to open source and a member of the JHipster development team. You can find him online @mraible and raibledesigns.com.

Okta Developer Blog Comment Policy

We welcome relevant and respectful comments. Off-topic comments may be removed.