Send computations from a web UI to an API, display the results.
This is a simple project to demonstrate usage of:
- (API) Java 21 (LTS) and Spring Boot
- (client) TypeScript and React (single-page application)
The API and the client are decoupled: their only touchpoint is the API contract. They could actually be two different projects in two different repositories.
The API could be deployed as a Docker image in an ECS instance while the client can be independently deployed to an S3 bucket served in a CloudFront distribution.
Of course, such simple project doesn't need horizontal scaling, but in a real setting you would wrap three ECS instances into a autoscaling group: that would also improve the API availability. One could use Kubernetes (AWS EKS) if already available, but for such a simple service it would be overkill.
A further way to improve scability and availability would be to rearchitect the API into AWS API Gateway and use Lambda functions for each operation.
The API is a project generated on https://start.spring.io/ with some other dependencies added on a ad-hoc basis.
mvn spring-boot:run
mvn spring-boot:start
mvn spring-boot:stop
I try to use Bun, currently at version 1.0.3 (so quite fresh, expect some rough edges). From the doc:
Bun is an all-in-one toolkit for JavaScript and TypeScript apps. It ships as a single executable called
bun
.At its core is the Bun runtime, a fast JavaScript runtime designed as a drop-in replacement for Node.js. It's written in Zig and powered by JavaScriptCore under the hood, dramatically reducing startup times and memory usage.
I followed:
and generated this project with:
bun create react-app client --template typescript
bun add -d bun-types
Start the frontend project with:
bun start
While very promising, it's not yet clear how to handle index.html
in Bun. Because of that, I default on Node to build a deployable bundle:
node run build
Then the bundle can be served locally, as if it were from a CloudFront distribution:
bunx serve build
Clean local Docker environments:
echo y | docker rm --force $(docker ps --all --quiet --filter status=exited)
echo y | docker volume prune
Start Docker environment containing local services:
docker compose up
# or, alternatively:
docker compose up --force-recreate --renew-anon-volumes
See docker-compose.yml for all services.
Now on your machine different services are available:
- Swagger UI: http://localhost:3001
- Grafana: http://localhost:3002
- Prometheus: http://localhost:9090
Grafana dashboards:
- https://grafana.com/grafana/dashboards/6756-spring-boot-statistics/
- Ideally there would be a standard dashboard for endpoints and another for database connections.
We use Spring Boot actuator to gather several metrics. It is exposed here: http://localhost:8080/actuator, scraped by Prometheus and exposed to Grafana as « local » datasource.
A load simulation is described using the Java DSL. You can run it with Gatling:
mvn gatling:test
Of course it doesn't have a lot of meaning to run such simulation a local computer with a lot of different possible perturbation. An isolated, network-optimised environment in AWS is more repeatable. Still, it can give a rough, uneducated hint at the performance. Keep in mind that your local machine may have a limitation on the number of open file descriptors (ulimit
), which further degrades the ability to simulate high load.
A note on AWS resources: instances should be in the same availability zone (ha ha), and Elastic Fabric Adapter network interface should help make the CPU and memory the limiting factors, not the network IO.
Using https://github.com/giltene/wrk2:
wrk --threads 2 --connections 10 --duration 45s --rate 3000 --latency http://localhost:8080/api/v1/addition\?a\=1\&b\=2 | wrk2img result-$(gdate +"%Y-%m-%dT%H:%M:%S.%3N").png
The API operation under test is so simple and pure (a mathematical addition) that this graph doesn't really test anything except the Spring Boot framework itself running on a local machine.
For further reference, please consider the following sections:
- Official Apache Maven documentation
- Spring Boot Maven Plugin Reference Guide
- Create an OCI image
- Spring Web
The following guides illustrate how to use some features concretely: