Download as pdf or txt
Download as pdf or txt
You are on page 1of 9

BROUGHT TO YOU IN PARTNERSHIP WITH

CONTENTS

∙ Key Benefits

∙ Getting Started

Getting Started ∙ Key Components

∙ JAX-RS

With Quarkus
∙ Health Checks

∙ Security and JWT

∙ Docker and Native

∙ Container Images

∙ And more!

ALEX SOTO ∙ Conclusion


DIRECTOR OF DEVELOPER EXPERIENCE, RED HAT

Quarkus is a Kubernetes-Native Java stack tailored to GraalVM and GETTING STARTED


OpenJDK HotSpot, helping Java programs run 10X faster, while being To create a Quarkus service, you just need to run the next Maven goal
100X smaller. Improving the developer experience, Quarkus provides into an empty directory:
additional features like live reloading and debugging as well as
mvn io.quarkus:quarkus-maven-plugin:1.13.1.Final:create
persistence with Panache.
\
-DprojectGroupId=org.acme \
Its integration with the Eclipse MicroProfile specification also makes
-DprojectArtifactId=hello-world \
it the perfect choice for developing microservices and deploying
-DclassName="org.acme.quickstart.GreetingResource" \
them in Kubernetes. -Dpath="/hello"

KEY BENEFITS LIVE RELOAD


Quarkus offers near-instant scale-up and high-density utilization in Quarkus applications come with a live reload feature that allows
container orchestration platforms such as Kubernetes. Many more the developer to make changes to their source code, which will be
application instances can be run using the same hardware resources. directly reflected in the deployed code without having to recompile
In Quarkus, classes used only at application startup are invoked at or repackage the source code.
build time and not loaded into the runtime JVM.

Quarkus also avoids reflection as much as possible. These design


principles reduce the size and memory footprint of an application
running on the JVM. Quarkus’ design accounts for native compilation
from the onset; optimization for using GraalVM, specifically its native
image capability, to compile JVM bytecode to a native machine
binary.

Additionally, Quarkus rests on a vast ecosystem of technologies,


standards, libraries, and APIs. Developers don’t have to spend lots
of time learning an entirely new set of APIs and technologies to take
advantage of the benefits Quarkus brings to the JVM or native images.

1
Build here.
Go anywhere.
SPONSOR FULL-PAGE AD HERE

Join now
REFCARD | GETTING STARTED WITH QUARKUS

./mvnw compile quarkus:dev • Creating an environment file named .env in the current
working directory (GREETING_MESSAGE=Namaste)
This service is started locally and you can use a curl command on
• Creating an external config directory under the current work-
the http://localhost:8080/hello URL to get a response from
ing directory (config/application.properties)
the service.
• Creating an application.properties file in the src/
You can make any changes in the org.acme.quickstart. main/resources directory within the project
GreetingResource source class, (i.e., change the return value from
hello to aloha), and you can execute a curl command on http:// You can set additional configuration files by using the smallrye.

localhost:8080/hello URL and get the updated response without config.locations property.

redeploying the service.


KEY COMPONTENTS
CONFIGURATION
JAX-RS
Quarkus uses the Eclipse MicroProfile Configuration spec as its
Quarkus uses the JAX-RS spec for implementing RESTful web services,
configuration mechanism. Quarkus applications are fully configured
as shown below:
using a single configuration file, which is located at src/main/
@Path("/developer")
resources/application.properties. You can create a custom
public class DeveloperResource {
property using the following code:

@GET
greetings.message=Hello World!!
@Produces(MediaType.APPLICATION_JSON)
public List<Developer> getAllDevelopers() {}
Then you can inject the configuration value into any class field:

@ConfigProperty(name = "greetings.message") @POST

String message; @Produces(MediaType.APPLICATION_JSON)


public Response createDeveloper(Developer
developer) {}
PROFILES

@DELETE
Quarkus has three different default profiles that can be set by starting
@Path("{id}")
the property with %: @Produces(MediaType.APPLICATION_JSON)
public Response deleteDeveloper(@PathParam("id")
• dev when running in quarkus:dev mode Long id) {}

• test when running tests


@GET
• prod (the default one) @Produces(MediaType.APPLICATION_JSON)
@Path("search")
quarkus.http.port=9090 public Response searchDeveloper(@
%dev.quarkus.http.port=8181 QueryParam("skills") String skills) {}
}
You can also create custom profiles by either setting the active
profile as a system property (quarkus.profile) or as an JSON MARSHALLING AND UNMARSHALLING
environment variable (QUARKUS_PROFILE). For example, Dquarkus.
profile=staging: To marshal and unmarshal Java objects to JSON format, you need to
register either the JSON-B or Jackson extension:
%staging.quarkus.http.port=9999
./mvnw quarkus:add-extension -Dextensions="resteasy-

Configuration values can be overridden at runtime (in decreasing jsonb"

priority):
Or:

• Using system properties (-Dquarkus.http.port) ./mvnw quarkus:add-extension -Dextensions="resteasy-


jackson"
• Using environment variables (QUARKUS_HTTP_PORT)

3 BROUGHT TO YOU IN PARTNERSHIP WITH


REFCARD | GETTING STARTED WITH QUARKUS

MICROPROFILE SPEC FOR CLOUD used in several elements, such as a CDI bean, JAX-RS resource, or
(KUBERNETES)-NATIVE APPLICATIONS MicroProfile REST Client.
RESTFUL WEB CLIENT
./mvnw
quarkus:add-extension -Dextensions="smallrye-fault-
Quarkus integrates with MicroProfile REST clients to invoke other
tolerance"
RESTful web services.

./mvnw quarkus:add-extension -Dextensions="resteasy- Possible strategies are:


jsonb, rest-client-jsonb"
OPERATION PROPERTIES ANNOTATION
Below is the code for creating an interface for the RESTful web
maxRetries, delay,
endpoint accessing http://worldclockapi.com/api/json/cet/now:
delayUnit, maxDuration,

package org.acme.greetings; Retry Policy @Retry durationUnit, jitter,


jitterDelayUnit,

@Path("/api") retryOn, abortOn

@RegisterRestClient
Fallback Action @Fallback fallbackMethod
public interface WorldClockService {
@GET
Timeout @Timeout unit
@Path("/json/{where}/now")
@Produces(MediaType.APPLICATION_JSON)
failOn, skipOn,
WorldClock getNow(@PathParam("where") String
delay, delayUnit,
where);
Circuit Breaker @CircuitBreaker requestVolumeThreshold,
}
failureRatio,
successThreshold
Then you need to configure the URL of the service in application.
properties by using the fully qualified name of the class: Bulkhead — waitingTaskQueue (only
Thread Pool/ @Bulkhead valid with @Asynchronous
org.acme.greetings.WorldClockService/mp-rest/ Semaphore semaphore mode)
url=http://worldclockapi.com

AUTOMATIC RETRIES
Last, you need to inject the interface into the classes that require this
client: An automatic retry is executed when the getNow method throws an

@Inject
exception.
@RestClient
@Retry(maxRetries = 1)
WorldClockService worldClockService;
@Fallback(fallbackMethod = "fallbackMethod")
WorldClock getNow(){}
Tip: If the invocation happens within JAX-RS, you can
propagate the headers' value from the incoming request to the public String fallbackMethod() {
outgoing response by specifying them in the org.eclipse. return WorldClock.now();

microprofile.rest.client.propagateHeaders }

parameter (i.e., org.eclipse.microprofile.rest.


The fallbackMethod must have the same parameters and
client.propagateHeaders=Authorization).
return type as the annotated method, as well as an additional

The REST client also supports asynchronous calls by returning ExecutionContext parameter.

CompletionStage<WorldClock> or Uni<WorldClock> (requires


Fallback logic can be implemented in a class instead of a method, as
the quarkus-rest-client-mutiny extension) instead of the POJO
shown below:
directly.
public class RecoverFallback implements

FAULT TOLERANCE FallbackHandler<WorldClock> {


@Override
It is really important to build fault-tolerant microservices when
public WorldClock handle(ExecutionContext context) {
they are deployed in Kubernetes where all communication happens }
within the network. Quarkus integrates with the MicroProfile }
fault-tolerance spec, which uses a CDI interceptor and can be

4 BROUGHT TO YOU IN PARTNERSHIP WITH


REFCARD | GETTING STARTED WITH QUARKUS

This type of logic can then be set in the annotation as the value @Liveness
@ApplicationScoped
@Fallback(RecoverFallback.class).
public class DatabaseHealthCheck implements

CIRCUIT BREAKER HealthCheck {


@Override

Circuit breaker is used to detect failures and encapsulate the logic, public HealthCheckResponse call() {
HealthCheckResponseBuilder responseBuilder =
preventing a failure to occur repeatedly.
HealthCheckResponse.named("Database conn");

@CircuitBreaker(requestVolumeThreshold = 4,
failureRatio = 0.75, delay = 1000) try {
checkDatabaseConnection();

@Fallback(fallbackMethod = "fallbackMethod") responseBuilder.withData("connection",

WorldClock getNow(){} true);


responseBuilder.up();

If three (4 x 0.75) failures occur among the rolling window of four }


catch (IOException e) {
consecutive invocations, the circuit is opened for 1000 milliseconds
// cannot access the database
and then returns to a half open state.
responseBuilder.down()
.withData("error", e.getMessage());
BULKHEAD
}
return responseBuilder.build();
Bulkhead pattern limits the number of concurrent calls to a
}
component. }

@Bulkhead(5)
Below is the code for accessing the /q/health endpoint to get a
@Retry(maxRetries = 4, delay = 1000, retryOn =
BulkheadException.class)
JSON document describing the status of the application:
WorldClock getNow(){}
{
"status": "UP",
CONFIGURATION "checks": [
{
You can override annotation parameters via the configuration file "name": "Database conn",
using the following property: "status": "UP",
"data": {
[classname/methodname/]annotation/parameter: "connection": true
# Method scope }
org.acme.greetings.WorldClockService/getNow/Retry/ }
maxDuration=30 ]
# Class scope }
org.acme.greetings.WorldClockService/Retry/
maxDuration=3000 The status code is 200 OK if the global status is UP and 503 Service
# Global
Unavailable if the status is DOWN. The /q/health/ready and /q/
Retry/maxDuration=3000
health/live endpoints can be used to get results individually from
readiness or liveness probes.
HEALTH CHECKS
Kubernetes relies on the concept of liveness and readiness probes Since health checks are CDI beans, you can create health checks
to monitor the state of the pod and decide if it should be restarted directly in a class:
or, for example, if it should start accepting traffic. Quarkus integrates
import javax.enterprise.inject.Produces;
with the MicroProfile Health spec to provide health checks to
import io.smallrye.health.HealthStatus;
Kubernetes.
public class ApplicationHealthChecks {
./mvnw quarkus:add-extension -Dextensions="smallrye-

health"
@Liveness
@ApplicationScoped
To create a custom health check, you need to implement the @Produces
HealthCheck interface and annotate the class with @Readiness
Continued on next page.
and/or @Liveness.

5 BROUGHT TO YOU IN PARTNERSHIP WITH


REFCARD | GETTING STARTED WITH QUARKUS

public HealthCheck liveCheck() { Metrics are accessible at /q/metrics/.


return HealthStatus.up("App");
}
TRACING
Quarkus uses OpenTracing (the MicroProfile OpenTracing spec)
@Readiness
@ApplicationScoped to provide distributed tracing for services distributed across a
@Produces Kubernetes cluster.
public HealthCheck dbHealthCheck() {
return HealthStatus.state("db", ./mvnw quarkus:add-extension

this::isDbUpAndRunning); -Dextensions="smallrye-opentracing"

}
The below code is used for tracing configuration:
private boolean isDbUpAndRunning() {}
quarkus.jaeger.service-name=myservice
}
quarkus.jaeger.sampler-type=const
quarkus.jaeger.sampler-param=1
Some extensions provide readiness probes by default. To mention
quarkus.jaeger.endpoint=http://localhost:14268/api/
some of them: DataSource, Kafka, Kafka-Streams, MongoDB, traces
Neo4J, Artemis, Vault, gRPC, Cassandra, and Redis.
By default, requests sent to any endpoint will be traced without any

METRICS code changes being required. You can customize traces by injecting

Quarkus can utilize the Micrometer metrics library for runtime and the Tracer class:

application metrics. @Inject


Tracer tracer;
./mvnw quarkus:add-extension -Dextensions="micrometer-
tracer.activeSpan().setBaggageItem("key", "value");
registry-prometheus"

If you want to disable tracing at the class or method level, you can
With the Prometheus registry extension added, the generated output
use the @Traced annotation.
format follows the Prometheus format.

Quarkus provides additional tracers:


By default, runtime metrics are generated automatically (i.e., HTTP
server connections, JVM memory, etc.), but application metrics can • JDBC Tracer: Adds a span for each JDBC query.
be generated too.
• You need to register the io.opentracing.
You can use io.micrometer.core.instrument.MeterRegistry contrib:opentracing-jdbc dependency and

to register metrics: configure the datasource url parameter with


jdbc:tracing:postgresql://localhost:5432/
private final MeterRegistry registry;
mydatabase and the driver parameter with
public PrimeNumberResource(MeterRegistry) registry) {
io.opentracing.contrib.jdbc.TracingDriver.
this.registry = registry;
registry.gauge(“prime.number.max”, this, • Kafka Tracer: Adds a span for each message sent to, or
PrimeNumberResource::highestObservedPrimeNumber);
received from, a Kafka topic.
}
• You need to register the io.opentracing.
Micrometer uses MeterFilter instances to customize the metrics contrib:opentracing-kafka-client dependency
emitted by MeterRegistry instances. Any MeterFilter CDI beans and configure incoming and outgoing interceptors
are detected and used when initializing MeterRegistry instances. with io.opentracing.contrib.kafka.
TracingProducerInterceptor and io.opentracing.
@Singleton
public class CustomConfiguration { contrib.kafka.TracingConsumerInterceptor.

@Produces
@Singleton SECURITY AND JWT
public MeterFilter configureAllRegistries() {
In microservices architectures, using stateless services is one of the
return MeterFilter.commonTags(Arrays.asList(
Tag.of(“env”, deploymentEnv)));
goals you'd like to achieve. For security and, more specifically, the
} authorization process, Quarkus integrates with the MicroProfile JWT
} RBAC spec.

6 BROUGHT TO YOU IN PARTNERSHIP WITH


REFCARD | GETTING STARTED WITH QUARKUS

./mvnw quarkus:add-extension -Dextensions="smallrye-jwt" DOCKER AND NATIVE


Quarkus's scaffolding project comes with Dockerfiles to generate
To send a token to the server-side, you should use the
Docker images for using Quarkus in JVM mode or in native mode.
Authorization header:

JVM MODE
curl -H \"Authorization: Bearer eyJraWQiOi... \".

In this mode, no native compilation is required and you simply run


The minimum JWT required claims are: typ, alg, kid, iss, sub, exp,
your application via a traditional jar.
iat, jti, upn, and groups.
./mvnw clean package
Quarkus ensures that the given token is valid (i.e., not manipulated, docker build -f src/main/docker/Dockerfile.jvm -t
not expired, from the correct issuer, etc.). JWT tokens can be injected quarkus/gettingstarted
.
into a JsonWebToken class in your code or using @Claim if a specific
claim is required.
NATIVE MODE
@Inject
JsonWebToken jwt; You can build a native image by using GraalVM. Quarkus can generate
@Inject a native executable of your application to be containerized into
@Claim(standard = Claims.preferred_username)
Docker without having to install GraalVM in your local machine.
String name;
./mvnw package -Pnative -Dnative-image.dockerbuild=true
To inject claim values, the bean must be @RequestScoped CDI
scoped. If you need to inject claim values into the scope with a docker build -f src/main/docker/Dockerfile.native -t

lifetime greater than @RequestScoped, then you need to use the quarkus/gettingstarted .

javax.enterprise.inject.Instance interface.
CONTAINER IMAGES
An example of the minimal configuration parameters is:
You can leverage Quarkus to generate and release container images.
mp.jwt.verify.publickey.location=META-INF/resources/ Three methods are supported based on the registered extension:
publicKey.pem
• Jib (GoogleContainerTools/jib: Build container images for

mp.jwt.verify.issuer=https://quarkus.io/using-jwt-rbac your Java applications.)


• ./mvnw quarkus:add-extension -Dexten-
PARAMETER DEFAULT DESCRIPTION sions=”quarkus-container-image-jib”

• Docker
quarkus.smallrye- Determine if the JWT
true • ./mvnw quarkus:add-extension -Dexten-
jwt.enabled extension is enabled.
sions=”quarkus-container-image-docker”
The name of the java.
• S2I (openshift/source-to-image: A tool for building artifacts
security.Provider
quarkus.smallrye- from source and injecting into container images)
SunRsaSign that supports
jwt.rsa-sig-provider
SHA256withRSA • ./mvnw quarkus:add-extension -Dexten-
signatures.
sions=”quarkus-container-image-s2i”
mp.jwt.verify. Public Key text itself to
publickey be supplied as a string.
PROPERTY DEFAULT DESCRIPTION

mp.jwt.verify. Relative path or URL of a quarkus.container- The group/repository of


${user.name}
publickey.location public key. image.group the image.

quarkus.container- The application


mp.jwt.verify. The name of the image.
iss accepted as valid. image.name name
issuer

quarkus.container- The application


The tag of the image.
The JWT groups claim is directly mapped to roles used in security image.tag version
annotations by using security annotations (i.e.,
@RolesAllowed("Subscriber")) Continued on next page.

7 BROUGHT TO YOU IN PARTNERSHIP WITH


REFCARD | GETTING STARTED WITH QUARKUS

quarkus.kubernetes.mounts.github-token.path=/
quarkus.container-
Additional tags of the deployment/github
image.additional-
container image. quarkus.kubernetes.mounts.github-token.read-only=true
tags

quarkus.kubernetes.secret-volumes.github-token.volume-
quarkus.container- The registry to use for
docker.io name=github-token
image.registry pushing.
quarkus.kubernetes.secret-volumes.github-token.secret-
name=greeting-security
quarkus.container- The username to access
quarkus.kubernetes.secret-volumes.github-token.default-
image.username the registry.
mode=420

quarkus.container- The password to access


quarkus.kubernetes.config-map-volumes.github-token.
image.password the registry.
config-map-name=my-secret

quarkus.container- Flag to allow insecure


false quarkus.kubernetes.expose=true
image.insecure registries.

quarkus.kubernetes.env.vars.my-env-var=foobar
quarkus.container- Boolean to set if the
false quarkus.kubernetes.env.configmaps=my-config-
image.build image should be built.
map,another-config-map
quarkus.kubernetes.env.secrets=my-secret,my-other-
quarkus.container- Boolean to set if the
false secret
image.push image should be pushed.

quarkus.kubernetes.resources.requests.memory=64Mi
To build and push a container image using the Quarkus extension, quarkus.kubernetes.resources.limits.cpu=1000m
you need to run the following command:
Resources can be added under the src/main/kubernetes directory.
./mvnw package -Dquarkus.container-image.push=true
PARAMETER DEFAULT DESCRIPTION
Tip: You can use native flags and container image
tags together to publish a container image containing quarkus.kubernetes. Generates and deploys the
false
a native image. deploy resources to Kubernetes.

Generates resources for


KUBERNETES AND QUARKUS INTEGRATION quarkus.kubernetes.
Kubernetes kubernetes, openshift, or
Quarkus can use the Dekorate project to generate Kubernetes deployment-target
knative.
resources.
To generate a container image, push it to a registry, and deploy the
./mvnw quarkus:add-extension -Dextensions="quarkus-
application, you can run the following command:
kubernetes"

./mvnw clean package -Dquarkus.kubernetes.deploy=true


By running ./mvnw package, the Kubernetes resources are created
at the target/wiring-classes/META-INF/kubernetes directory.
REMOTE LIVE RELOAD
Then the generated resources are integrated with the MicroProfile
Live reload also works with remote instances (even when deployed
Health and Metrics annotations.
inside a Kubernetes cluster) if it is configured properly in src/main/
You can customize the generated resources by setting new values in resources/application.properties:
application.properties:
# Mutable Jar configurations

quarkus.kubernetes.namespace=mynamespace quarkus.package.type=mutable-jar
quarkus.live-reload.password=changeit

quarkus.kubernetes.replicas=3
quarkus.container-image.build=true

quarkus.kubernetes.labels.foo=bar quarkus.kubernetes-client.trust-certs=true
quarkus.kubernetes.expose=true

quarkus.kubernetes.annotations.foo=bar quarkus.kubernetes.env-vars.quarkus-launch-devmode.
value=true

quarkus.kubernetes.readiness-probe.period-seconds=45
Notice the application is configured to start in dev mode — even

Continued on next column.


8 BROUGHT TO YOU IN PARTNERSHIP WITH
REFCARD | GETTING STARTED WITH QUARKUS

in production mode — by setting the quarkus-launch-devmode CONCLUSION


environment variable to true. Java has evolved since its introduction in 1995, but more importantly,
the environments hosting applications have evolved as well. Some
With application deployed in the cluster, get the public URL to access
trade-offs made in Java’s design at the onset affect how Java is
the service and set it in the configuration file:
perceived today. Things that were important in those days aren’t
quarkus.live-reload.url=http://YOUR_APP_ROUTE_URL as important anymore. Quarkus was invented given the challenges
and problems of today and aims to solve those challenges without
Start the application in remote-dev mode:
forcing developers to learn an entirely new programming language.

./mvnw quarkus:remote-dev
This Refcard has shown how to use some of the most common

Now any changes made locally will automatically be reloaded by the extensions within the Quarkus ecosystem. Please visit https://code.

application in the Kubernetes cluster. quarkus.io for a list of all extensions as well as https://qurkus.io/
guides/ for further information on building Quarkus applications.

WRITTEN BY ALEX SOTO,


DIRECTOR OF DEVELOPER EXPERIENCE, RED HAT

Alex is a Director of Developer Experience at Red Hat. He is passionate about Java and software automation, and he believes
in the open source software model.

Alex is the creator of the NoSQLUnit project, a member of the JSR374 (Java API for JSON Processing) Expert Group, the
co-author of the Testing Java Microservices book from Manning, and a contributor to several open source projects. A Java
Champion since 2017, international speaker and teacher at Salle URL University, he has talked about new testing techniques
for microservices and continuous delivery in the 21st century.

DZone, a Devada Media Property, is the resource software Devada, Inc.


developers, engineers, and architects turn to time and 600 Park Offices Drive
again to learn new skills, solve software development Suite 150
problems, and share their expertise. Every day, hundreds of Research Triangle Park, NC 27709
thousands of developers come to DZone to read about the
888.678.0399 919.678.0300
latest technologies, methodologies, and best practices. That
makes DZone the ideal place for developer marketers to Copyright © 2020 Devada, Inc. All rights reserved. No part
build product and brand awareness and drive sales. DZone of this publication may be reproduced, stored in a retrieval
clients include some of the most innovative technology and system, or transmitted, in any form or by means of electronic,
tech-enabled companies in the world including Red Hat, mechanical, photocopying, or otherwise, without prior
Cloud Elements, Sensu, and Sauce Labs. written permission of the publisher.

9 BROUGHT TO YOU IN PARTNERSHIP WITH

You might also like