Supersonic Subatomic Java

 

Peter Palaga

@ppalaga

Peter Palaga

Agenda

  • An 8 ms demo
  • What is Quarkus
  • How it works
  • More Quarkus

Demo

Demo



$ which docker # make sure you have docker
~/bin/docker
$ mvn clean package -Pnative -Dnative-image.docker-build
$ ls -lh target/*-runner
-rwxrwxr-x. 1 ppalaga ppalaga 19M Mar 20 14:39 target/quarkus-hello
$ ./target/*-runner
INFO  [io.quarkus] (main) Quarkus 0.22.0 started in 0.004s.
INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]


https://quarkus.io/guides/building-native-image-guide

What is Quarkus

Toolkit

and

Framework

for writing Java, Kotlin and Scala applications

Embracing existing standards

Java EE MicroProfile Spring
  • Servlet
  • JAX-RS
  • JPA, JDBC
  • CDI
  • Bean Validation
  • Transactions
  • Fault Tolerance
  • Health
  • JWT
  • Metrics (Prometheus)
  • OpenAPI
  • OpenTracing (Jaeger)
  • Reactive Messaging (Kafka, AMQP, MQTT)
  • Rest Client
  • Spring DI1
  • Spring Web1
  • Spring Data1

1) via compatibility layers
Well established

Libs and frameworks

Eclipse Vert.x Netty Apache Camel Infinispan Caffeine Keycloak
Kubernetes AWS Lambda Azure Functions Apache Tika ElasticSearch Kogito

Databases


  • MySQL, MariaDB
  • PostgreSQL
  • MS SQL Server
  • H2
  • FlyWay
  • Amazon DynamoDB‎
  • MongoDB
  • Neo4j
Unifies

Imperative

and

Reactive    

@Inject
SayService say;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
    return say.hello();
}
@Inject @Stream("kafka")
Publisher<String> reactiveSay;

@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public Publisher<String> stream() {
    return reactiveSay;
}

Developer joy

  • Easy to start with:
    mvn quarkus:create or code.quarkus.io
  • Live reload:
    mvn compile quarkus:dev
  • Java, Kotlin or Scala
  • Maven or Gradle
  • Runners for JUnit 5
  • Isolation from GraalVM CLI/API

Container First

💾 Small size on disk ✓ Small container images
🚀 Fast boot time ✓ Instant scale up
🔬 Low memory footprint ✓ More containers with the same RAM

Measuring memory



RSS1 = all RAM consumed by the process
$ ps -o pid,rss,command -p $(pgrep quarkus)
  PID   RSS COMMAND
11229 12628 ./target/quarkus-hello

 

1) Resident Set Size

Memory (RSS)

Quarkus + GraalVM Quarkus + OpenJDK Traditional Cloud-Native Stack
REST 13 MB 74 MB 140 MB
REST+JPA 35 MB 130 MB 218 MB

Startup time

Modern frameworks use lazy initialization

"started" before all classes are initialzed

What matters:

Time to first request

Time to first request1/2

@Path("/")
public class GreetingEndpoint {
    @GET @Path("/greeting")
    public String greeting(@QueryParam("name") String name) {
        System.out.println(System.currentTimeMillis());
        return "Hello " + name;
    }
}

Time to first request2/2

$ while [[ \
    "$(curl -s -o /dev/null -w '%{http_code}' localhost:8080/hello)" \
       != "200" \
  ]]; do sleep .00001; done
$ echo $(($(date +%s%N)/1000000)) && target/*-runner
1552931436155
INFO  [io.quarkus] (main) Quarkus 0.22.0 started in 0.035s.
INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, resteasy-jsonb]
1552931436637
$ expr 1552931436637 - 1552931436155
482      # Time to first request in milliseconds

 Time to first request

REST
REST + JPA

How it works

Traditional app server

  • Hundreds of classes run only during the boot
  • Later unused
  • Still occupy memory

XML parsers, annotation lookups, management model, ...

Quarkus:

Build time boot

As much work as possible done at build time

Output: recorded wiring bytecode

An example:

Build time CDI (1/2)



An example:

Build time CDI (2/2)

// User beans
@ApplicationScoped
class Catalog {

  @Inject
  Pricing pricing;

  @PostConstruct
  void init() {}
}



@ApplicationScoped
class Pricing { ... }




// Metadata (generated as bytecode)
class Catalog_Bean {

  Pricing_Bean pricingBean;

  Catalog create() {
    Catalog catalog = new Catalog();
    catalog.pricing =
               pricingBean.create();
    catalog.init();
    return catalog;
  }
}

class Pricing_Bean {...}




// The "main" class
// In reality generated as bytecode, here strongly simplified
class Application {
  static Map<String, Bean> beans = new ...Map();
  static Map<String, Supplier<Object>> appContext = new ...Map();
  static {
    // Wire the beans
    Pricing_Bean pricingBean = new Pricing_Bean();
    beans.put(Pricing_Bean.key, pricingBean);
    Catalog_Bean catalogBean = new Catalog_Bean();
    catalogBean.pricingBean = pricingBean;
    beans.put(Catalog_Bean.key, catalogBean);

    // Some beans can even be instantiated already
    Pricing pricing = pricingBean.create();
    appContext.put(Pricing_Bean.key, () -> pricing)
    Catalog catalog = catalogBean.create();
    appContext.put(Catalog_Bean.key, () -> catalog)
  }}

Wiring code invocation

Decided by extensions

static initializer OR main()
preferred access to files,
sockets, etc.

Build oriented container

Quarkus extensions


  • Units of Quarkus distribution (your Maven deps)
  • They configure, boot and integrate a lib/framework into a Quarkus application
  • Make the code lighter to run on a JVM
  • Make the code fit for the GraalVM

Quarkus Extensions ecosystem


  • New extensions are welcome!
  • Follow the guide for extension authors
  • Can be developed externally → Camel Quarkus
  • Soon: 3rd party extensions on code.quarkus.io and in the Quarkus universe BoM
GraalVM

Two distinct things:

  1. Graal compiler (AoT or JIT)
  2. GraalVM - the polyglot VM
In this presentation:

GraalVM

=

Graal compiler

+

SubstrateVM

AoT1 compilation with GraalVM

1) Ahead-of-time (1/3)

AoT compilation with GraalVM

(2/3)

  • Static analysis
  • Closed world assumption
  • Dead code elimination:
        classes, fields, methods, branches

AoT compilation with GraalVM

(3/3)

$ native-image -jar my-app.jar
$ ./my-app
GraalVM

Limitations

GraalVM

Response latency

  • No JIT (just in time) compilation on SubstrateVM
  • Stock JVMs:
    • Slower to boot, need more RAM
    • But faster to serve after warmup
  • Measure your use case

Efficiency through density

in cloud environments

GraalVM GraalVM

Dynaminc Classloading



Deloying jars, wars, etc. at runtime impossible

GraalVM GraalVM

JVMTI, JMX

+ other native VM interfaces



No agents

JRebel, Byteman, profilers, tracers, ...

GraalVM GraalVM

JVMTI, JMX

+ other native VM interfaces

  • No Java debugger
  • Native debugger (GDB) still works
  • DWARF debug symbols
    • Inserted only by GraalVM enterprise
    • Allows stepping through your java code
GraalVM GraalVM

Miscellaneous



Security Manager

finalize() (deprecated anyway)

InvokeDynamic and MethodHandles

GraalVM GraalVM

Reflection



Requires registration via native-image CLI/API

@RegisterForReflection in Qarkus

GraalVM GraalVM

More



Require registration via native-image CLI/API

  • Dynamic proxies
  • Resources
  • JNI, Unsafe Memory Access, ...
GraalVM GraalVM

Static init

1/2

Build time OpenJDK instance:

  • Resolve classes, run "safe" static initilizers
  • Take a snapshot of the produced heap
  • Store it in the executable
GraalVM GraalVM

Static init

2/2

Downsides:

no file handles, sockets, threads

What Quarkus shields you from

$ native-image -jar my-app.jar \
    -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime \
    -J-Djava.util.concurrent.ForkJoinPool.common.parallelism=1 \
    -H:FallbackThreshold=0 \
    -H:ReflectionConfigurationFiles=...
    -H:+ReportExceptionStackTraces \
    -H:+PrintAnalysisCallTree \
    -H:-AddAllCharsets \
    -H:EnableURLProtocols=http \
    -H:-JNI \
    -H:-UseServiceLoaderFeature \
    -H:+StackTrace \
    --no-server \
    --initialize-at-build-time=... \
    -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager \
    -J-Dio.netty.leakDetection.level=DISABLED \
    -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory \
    -J-Dsun.nio.ch.maxUpdateArraySize=100 \
    -J-Dio.netty.allocator.maxOrder=1 \
    -J-Dvertx.disableDnsResolver=true

More Quarkus!

Native

  • Either have docker (easier)
  • Or export GRAALVM_HOME=...
$ mvn clean package -Dnative -Dnative-image.docker-build
$ ls -lh target/*-runner
-rwxrwxr-x. 1 ppalaga ppalaga 19M Mar 20 14:39 target/quarkus-hello
$ ./target/*-runner
INFO  [io.quarkus] (main) Quarkus 0.22.0 started in 0.005s.
INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]


https://quarkus.io/guides/building-native-image-guide

Discover extensions

$ mvn quarkus:list-extensions
...
[INFO] Available extensions:
...
[INFO]   * Hibernate ORM (io.quarkus:quarkus-hibernate-orm)
[INFO]   * Hibernate ORM with Panache (io.quarkus:quarkus-hibernate-orm-panache)
[INFO]   * Hibernate Validator (io.quarkus:quarkus-hibernate-validator)
...

https://quarkus.io/guides/maven-tooling.html

Add an extension

$ mvn quarkus:add-extension -Dextensions=hibernate-orm-panache

https://quarkus.io/guides/maven-tooling.html

Panache

Makes simple Hibernate ORM easy

@Entity
public class Person extends PanacheEntity {
    public String name;
    public LocalDate birth;
    public PersonStatus status;
}

https://quarkus.io/guides/hibernate-orm-panache-guide

Panache persist

// Create a person
Person person = new Person();
person.name = "Stef";
person.birth = LocalDate.of(1910, Month.FEBRUARY, 1);
person.status = Status.Alive;

// Persist and delete
person.persist();
if (person.isPersistent()){
  person.delete();
}

https://quarkus.io/guides/hibernate-orm-panache-guide

Panache Queries

List<Person> allPersons = Person.listAll(); // All persons
Person p = Person.findById(personId); // Specific person by ID
// Living persons
List<Person> livingPersons = Person.list("status", Status.Alive);

// Count
int countAll = Person.count();
int countAlive = Person.count("status", Status.Alive);

// Delete
Person.delete("status", Status.Alive);
Person.deleteAll();

https://quarkus.io/guides/hibernate-orm-panache-guide

More Panache



  • Paging
  • Sorting
  • Simplified query language
  • Autogenerated DAO/Repositories


https://quarkus.io/guides/hibernate-orm-panache-guide

Quarkus wrap up

 

  • Good old Java
  • Good old standards and libs
  • Efficiency level of Go language

 

Makes Java the choice #1 for the cloud and serverless

Quarkus Links