/bookstore-reactive_spring

Reactive version of Bookstore Spring

Primary LanguageJava

Bookstore in a microservices way

This projects illustrates how to use Spring boot on a simple use case.

It is based on 2 (micro)services:

  • Books: an ebook store running on port 8082

  • Numbers: a backend service called by Books generating ISBN numbers. Runs on port 8081.

It is inspired from Antonio Goncalvez fascicule project. It has been firstly revamped in this project and then refurbished in a reactive way in this project During the demo, we will use Numbers "as is". The focus will be put on Books.

Requirements

This demo has been developed with the following piece of software:

  • OpenJDK 11.0.10

  • Spring Boot 2.5.0

  • Docker 20.10.5

  • Docker-compoose 1.28.5

  • curl 7.64.1

  • jq 1.6 (possible alternative: python -m json.tool)

  • Gradle 7.0

Preparing the demo

This preparation should be done before running the live demo.

Package Books

gradle build -p rest-book

Package Numbers

gradle build -p rest-numbers

Start the infrastracture

The infrastructure is made of:

  • PostgreSQL as database

  • Promotheus as backend for metrics

  • Jaeger as backend for OpenTracing.

To start it:

cd infrastructure
docker-compose up -d

It may take a few minutes if the images have not been yet downloaded.

To check the infrastructure:

docker-compose ps

You should see the 3 containers running.

To stop it:

cd infrastructure
docker-compose down

Start Numbers

cd rest-number
java -jar build/libs/rest-number-0.0.1-SNAPSHOT.jar

Start Books

cd rest-book
java -jar build/libs/rest-book-0.0.1-SNAPSHOT.jar

Demo 1: Health Check

All script (*.sh) mentionned below are in the bin directory.

Tribute to Josh Long: see how beautiful the banner is!

Since we’re running a packaged JAR file, the code is in "default" mode.

Check that Books is ready:

curl -s http://localhost:8082/actuator/health/readiness  | jq

We can see 2 live probes:

  • one relative to the database connection automatically provided by Spring

  • an other one related to an application specific maintenance flag.

By default the maintenance flag is set to false. That means that the service is ready.

There is an endpoint enabling to read and set that flag.

Let’s put the service in maintenance (setMaintenance.sh):

curl -X 'PUT' \
  'http://localhost:8082/api/maintenance' \
  -w "\n" \
  -H 'Content-Type: text/plain' \
  -d 'true' -v

We get an HTTP 204 answer (no-content).

We can check now that Books is no longer ready (checkMaintenance.sh):

curl -s http://localhost:8082/q/health/ready | jq

Let’s inspect the code of MaintenanceController.retreiveInMaintenance() to see how the HealthCheck probe is developped.

Let’s try to read a random book (randomBook.sh):

curl -s \
  -w "\n" \
  'localhost:8082/api/books/random' | jq

We get an answer from Books:

{
  "reason": "Service currently in maintenance"
}

Let’s browse the code of CheckMaintenanceFilter.java and see how this answer is provided using a JAX-RS filter.

Let’s reopen the service (unsetMaintenance.sh):

curl -X 'PUT' \
  'http://localhost:8082/api/maintenance' \
  -w "\n" \
  -H 'Content-Type: text/plain' \
  -d 'false' -v

We can now check now that Books is ready again (checkMaintenance.sh̀):

curl -s http://localhost:8082/q/health/ready | jq

Demo 2: inside the code

Let’s read a random book (randomBook.sh):

curl -s -w "\n" 'localhost:8082/api/books/random' | jq

Browse the following source to see how its is implemented:

Demo 3: Focus on OpenAPI and Swagger UI

Let’s see the OpenAPI documentation (openapi.sh):

curl -s -w "\n" localhost:8082/v3/api-docs | jq

Browse the following files to see how OpenAPI is coded:

OpenAPI/Swagger is enabled by default. You can disable it by applying this property

# Disable Swagger UI for the demo
springdoc.api-docs.enabled=false

Some configuration parameters can be overriden at runtime. For instance:

java -Dserver.port=9080 -Dspringdoc.api-docs.enabled=false -jar build/libs/rest-book-0.0.1-SNAPSHOT.jar

However not all parameters can be overiden in such way. In particular, Swagger UI cannot be enabled/disabled at runtime.

Use your favorite browser and go to:

http://localhost:8082/swagger-ui.html

Have a quick test with GET API Books (list all books).

Demo 4: Calling Numbers with RestTemplate

Numbers is called by Books on book creation (createBook.sh):

curl -s -w "\n" -X POST -d '{"title":"Practising Quarkus", "author":"Antonio Goncalves", "yearOfPublication":"2020"}' -H "Content-Type: application/json" localhost:8082/api/books -v

We’ve got a 202 status code (Created) and a link to the created resource provided with the Location header parameter.

Let’s read it:

curl -s -w "\n" localhost:8082/api/books/1 | jq

How does it work behind the scene? We make use of Spring RestTemplate

Browse BookService.java , BookConfiguration.java and application.yml to see how it is coded:

Demo 5: Fault Tolerance

So far, so good. But what if, Numbers is out of order? Let’s kill it …​ and try to create a book again.

Now we’ve got a 202 (Accepted) status code: the request has been accepted but the book has not been created, because no ISBN numbers have been provided.

What does it mean? In fact, we’ve entered a fallback mode: the book data have been stored in a file for later processing:

ls -l rest-book/book-*

Browse BookService.java, BookConfiguration and BookController.java to see how FaultTolerance is coded:

Other features from FaulTolerance (not in the demo): Timeout, CircuitBreaker, Retry, BulkHead, Asynchronous.

Demo 6: OpenTracing & Jaeger

Let’s switch to an important topic: observability and more specifically tracing.

Connect to the Jaeger GUI from your browser:

http://localhost:16686/

Jaeger is a distributed tracing system developped by Uber and donated to CNCF. It can be used for:

  • Distributed context propagation

  • Distributed transaction monitoring

  • Root cause analysis

  • Service dependency analysis

  • Performance / latency optimization

Let’s create a book again (createBook.sh):

curl -s -w "\n" -X POST -d '{"title":"Practising Quarkus", "author":"Antonio Goncalves", "yearOfPublication":"2020"}' -H "Content-Type: application/json" localhost:8082/api/books -v

Let’s search traces for Books. We can see how long has been spent in Books and Numbers.

By default, all REST endpoints are traced. No code is needed. You just have to add the Quarkus extension, to configure it and to run a backend system such as Jaeger (or Zipkin). It is also possible to annotate methods or classes with @Traced. Browse BookService.java.

Traces can also been enabled on JDBC at the risk of extreme verbosity.

OpenTracing must be configured in application.properties: it is possible to trace all or only parts of the requests.

Under the cover, context propagation is based on a specific HTTP header uber-trace-id.

Demo 7: Metrics & Promotheus

Metrics is another aspect of observability.

It must be configured in application.yml

management:
  auditevents:
    enabled: true
  endpoint:
    shutdown:
      enabled: true
    health:
      enabled: true
      probes:
        enabled: true
      show-details: always
    prometheus:
      enabled: true
    metrics:
      enabled: true
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true
  metrics:
    web:
      client:
        request:
          autotime:
            enabled: true

We can also gather rest endpoints metrics such as the time to respond.

Base metrics are about the JVM (classes, threads, gc)

curl -s http://localhost:8082/actuator/metrics/
curl -s http://localhost:8082/actuator/metrics/http.server.requests

Vendor metrics provides complementary technical metrics (cpu load, memory):

curl -s http://localhost:8082/actuator/metrics/system.cpu.usage
curl -s http://localhost:8082/actuator/metrics/jvm.memory.used

It is also possible to add custom application metrics, such as a rest controller metrics. To start aggregating them, you have to add the annotation @Timed to the controller

Browse BookController.java to see how it is implemented

curl -s http://localhost:8082/actuator/metrics/bookController

In contrast to OpenTracing, there is no default application metric. Methods have to be explicitelly annotated to generate metrics.

Curling metrics is limited to the current values, we have no historic. Let’s use Prometheus to collect metrics in a smart way. Prometheus is a metrics-based monitoring and alerting system, initially developed at SoundCloud and now hosted by the CNCF. It is internally based on Time Series Database.

Connect to the Prometheus GUI from your browser:

http://localhost:9090/graph

We can select a metric and do a graph with it. We can see different kinds of metrics:

  • counters: how much?

  • timers: how long?

Prometheus offers a basic GUI and it is recommended to use Grafana in production.

Browse BookController.java to see how Metrics is coded.

Demo 9: Testing

Browse BookControllerIT.java to see how to test using H2 in memory database and MockRestServiceServer