Comparison between Quarkus and Spring Boot
using a simple Reactive RestFul Api.
- Requirements
- Tech Stack
- Build and Run
- Swagger UI
- Quarkus vs Spring Boot Comparision
- Docker Compose
- Jmeter
- Kubernetes/Istio
- Naive Stress Test
- References
To compile and run this project you will need:
- JDK 8+
- Docker
- Mongodb
- Maven 3.6.3
- Keystore(or use
auth profile
to generate one automatic)
- Reactive RestFul API
- MongoDB
- Security(JWT)
- Swagger
On the root folder compile the source code for both projects.
mvn clean compile
If you are not using auth profile
you will need to place a public key in /tmp/publicKey.pem
or setting path PUBLIC_KEY_PATH
.
If you are using auth profile
it will generate a keystore(if not specified in PRIVATE_KEY_PATH and PUBLIC_KEY_PATH
) and expose endpoint /api/auth
.
QUARKUS_PROFILE=auth mvn compile quarkus:dev
or
mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=auth"
Quarkus has a nice hot deployment feature
that you can easily develop on the fly
without restarting the service to recompile the classes, that's kind of unusual for Java but very common feature for interpreted languages like PHP/Python
.
cd quarkus
mvn compile quarkus:dev
PS: To change default port(8081
) set QUARKUS_HTTP_PORT
.
Spring Boot has also hot deployment feature
but need some interaction from the IDE, more details look at Hot Swapping.
cd spring-boot
mvn spring-boot:run
PS: To change default port(8080
) set mvn spring-boot:run -Dspring-boot.run.arguments="--server.port={PORT}"
.
To access Swagger UI and generate a valid JWT use /api/auth
when auth profile
is on.
Use following roles:
ROLE_ADMIN
- Access for all endpointsROLE_COMPANY_READ
- Read Access toGET - /api/companies
andGET - /api/companies/{id}
.ROLE_COMPANY_CREATE
- Create Access toPOST - /api/companies
ROLE_COMPANY_SAVE
- Update Access toPUT - /api/companies
ROLE_COMPANY_DELETE
- Delete Access toDELETE - /api/companies
PS: To generate a JWT first need to Logout
on Authorize Button.
- Quarkus
Quarkus has support for JAX-RS
with RESTEasy framework also Spring Web Annotations
.
Click here to expand...
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/api/example")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ExampleResource {
@GET
public Response get() {return Response.ok().entity(something).build(); }
}
- Spring Boot
Spring also supports JAX-RS
but more commonly used is with Spring Web Annotations
.
Click here to expand...
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/example")
public class ExampleController {
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> get() {
return ResponseEntity.ok(something);
}
}
- Quarkus
Quarkus is reactive by default using Vert.x under the hood, the reactive rest support is using Mutiny also has support for others libraries like RxJava and Reactor
.
Click here to expand...
public class CompanyResource {
public Multi<Company> getAllActiveCompanies() {
return Company.streamAll();
}
public Uni<Response> getById(@PathParam("id") String id) {
return Company.findById(new ObjectId(id))
.onItem().ifNull().failWith(NotFoundException::new)
.map(c -> Response.ok(c).build());
}
}
Full example look at CompanyResource.
- Spring Boot
Spring uses Reactor for reactive programming but also other libraries like RxJava
.
Click here to expand...
public class CompanyController {
public Flux<Company> getAllActiveCompanies() {
return companyRepository.findAll();
}
public Mono<Company> getById(@PathParam("id") String id) {
return companyRepository.findById(id)
.flatMap(Mono::just)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)));
}
}
Full example look at CompanyController.
- Quarkus
Quarkus uses Panache and provides Active Record Pattern style and repository
, it's a nice library but has a preview
and is not backward compatibility.
Click here to expand...
@MongoEntity(collection = "quarkus_companies")
public class Company extends ReactivePanacheMongoEntity implements Serializable {
@NotBlank
public String name;
public boolean activated;
public static Multi<Company> findActiveCompanies(Integer pageSize) {
return find("activated", true)
.page(Page.ofSize(pageSize))
.stream();
}
}
- Spring Boot
Spring uses Spring Data and repository style
but also support other libraries like RxJava
.
Click here to expand...
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "springboot_companies")
public class Company implements Serializable {
@Id
private String id;
private String name;
private Boolean activated;
}
@Repository
public interface CompanyRepository extends ReactiveMongoRepository<Company, String> {
@Query("{'activated': true}")
Flux<Company> findActiveCompanies(final Pageable page);
}
- Quarkus
Quarkus has a good support for OpenApi and Swagger UI
just needed library quarkus-smallrye-openapi
and a few annotations.
Click here to expand...
@OpenAPIDefinition(tags = {@Tag(name="company", description="Operations related to companies")})
@SecuritySchemes(@SecurityScheme(securitySchemeName = "api_key",
type = SecuritySchemeType.APIKEY,
apiKeyName = "Authorization",
in = SecuritySchemeIn.HEADER)
)
public class ResourceApplication extends Application {
}
Full example look at ResourceApplication.
- Spring Boot
Spring Boot has some issues using WebFlux with Swagger UI
and no default support for OpenApi.
To achieve Swagger UI was necessary to use SpringFox SNAPSHOT version
.
-
Quarkus
Quarkus basic has @QuarkusTest
annotation and recommends to use rest-assured
but had some issues to test reactive rest apis with security(jwt).
Had some issues also to test Embedded MongoDb
needed to add a class to start mongodb manually.
Click here to expand...
public class MongoTestResource implements QuarkusTestResourceLifecycleManager {
private static MongodExecutable MONGO;
private static final Logger LOGGER = Logger.getLogger(MongoTestResource.class);
@Override
public Map<String, String> start() {
//StartMongoDb
}
}
@QuarkusTest
@QuarkusTestResource(MongoTestResource.class)
public class CompanyResourceTest {
}
Full example look at MongoTestResource and CompanyResourceTest.
- Spring Boot
Spring Boot has a good testing api using @SpringBoot, @DataMongoTest and @WebFluxTest
, more details look at spring tests.
- Quarkus
Quarkus uses Microprofile annotations
like @Timed, @Metered, @Counted
but need to add it manually for each endpoint, more details look at CompanyResource.
- Spring Boot
Spring Boot uses micrometer
and has easy integration with prometheus just need library micrometer-registry-prometheus
.
- Quarkus
It has by default support to build a docker image and a native image also. To build a docker image:
cd quarkus
mvn clean package -Dquarkus.container-image.build=true
docker build -f src/main/docker/Dockerfile.jvm -t quarkus .
To build native image
mvn package -Pnative -Dquarkus.native.container-build=true
docker build -f src/main/docker/Dockerfile.native -t quarkus .
PS: The native image didn't work for me using laptop i7 16GB dell inpiron
.
- Spring Boot
Spring Boot doesn't have a built-in docker image but can easily use jib or fabric8
.
To build a docker image using fabric8 plugin:
cd spring-boot
mvn clean install docker:build
I've used 2 replicas for each service and deployed pods to GKE.
- Quarkus
Quarkus is really fast to startup as you can see in attached images below, it took less than 5 seconds. Replica 1
Replica 2
- Spring Boot
Spring Boot is not so fast as Quarkus but also not too slow it took average 23 seconds to startup. Replica 1
Replica 2
-
Quarkus
-
Quarkus was slower than Spring Boot to handle the requests but I believe if I change the connection pool would perform better.
-
Spring Boot
I've also tested with 3 replicas and had similar results.
- Quarkus
3 Replicas
Boot Time
Result
- Spring Boot
3 Replicas
Result
Topics | Winner - IMHO :) |
---|---|
RestFul API Support | draw |
Reactive Programming Support | |
MongoDb Reactive Support | |
Swagger Support | |
Testing | |
Metrics - Prometheus | |
Docker Build | |
Performance Test |
Once the docker images are build you can use docker-compose
to run both services with mongodb, prometheus and grafana
.
Check jmeter folder to run performance tests and see the results for both services.
Check k8s folder for kubernetes/minikube and istio setup.
cd docker-compose
docker-compose up -d
- Create
./naive-stress-test.sh -c 1 -r 1 \
-a localhost:8080/api/companies \
-X POST \
-H "Authorization: bearer XXXX" \
-H "Content-Type: application/json" \
-d '{"name": "test curl", "activated": true}'
- Create appending body with microseconds
./naive-stress-test.sh -c 1 -r 1 \
-a localhost:8080/api/companies \
-X POST \
-H "Authorization: bearer XXXX" \
-H "Content-Type: application/json" \
-d '{"name": "test curl - #INCREMENT_TIMESTAMP", "activated": true}'
- Get
./naive-stress-test.sh -c 1 -r 1 \
-a localhost:8080/api/companies \
-X GET \
-H "Authorization: bearer XXXX" \
-H "Content-Type: application/json"