Supersonic Subatomic Java
Peter Palaga
$ export GRAALVM_HOME=...
$ mvn clean package -Pnative
$ 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.11.0 started in 0.007s.
INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
Quark - an elementary particle
us - the hardest problem in software
|
||
|
|
Eclipse Vert.x | Hibernate | Resteasy | Apache Camel | Netty |
Kubernetes | Jaeger | Prometheus | Apache Kafka | Infinispan |
Unifies | ||||
Imperative |
and | Reactive |
||
|
|
💾 | Small size on disk | ✓ | Small container images |
🚀 | Fast boot time | ✓ | Instant scale up |
🔬 | Low memory footprint | ✓ | More containers with the same RAM |
$ ps -o pid,rss,command -p $(pgrep quarkus)
PID RSS COMMAND
11229 12628 ./target/quarkus-hello
1) Resident Set Size
Quarkus + GraalVM | Quarkus + OpenJDK | Traditional Cloud-Native Stack | |
REST | 13 MB | 74 MB | 140 MB |
REST+JPA | 35 MB | 130 MB | 218 MB |
Modern frameworks use lazy initialization
"started" before all classes are initialzed
What matters:
@Path("/")
public class GreetingEndpoint {
@GET @Path("/greeting")
public String greeting(@QueryParam("name") String name) {
System.out.println(System.currentTimeMillis());
return "Hello " + name;
}
void onStart(@Observes StartupEvent startup) {
System.out.println(System.currentTimeMillis());
}
}
$ while [[ \
"$(curl -s -o /dev/null -w '%{http_code}' localhost:8080/greeting)" \
!= "200" \
]]; do sleep .00001; done
$ echo $(($(date +%s%N)/1000000)) && target/*-runner
1552931436155
1552931436626
INFO [io.quarkus] (main) Quarkus 0.11.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
REST |
REST + JPA |
XML parsers, annotation lookups, management model, ...
As much work as possible done at build time
Output: recorded wiring bytecode
Decided by extensions
static initializer |
OR | main() |
preferred | access to files, sockets, etc. |
Two distinct things:
1) Ahead-of-time (1/3)
(2/3)
(3/3)
$ native-image -jar my-app.jar
$ ./my-app
🚀 Fast process start |
🔬 Less memory |
💾 Small size on disk |
Deloying jars, wars, etc. at runtime impossible
No agents
JRebel, Byteman, profilers, tracers, ...
Security Manager
finalize()
(deprecated anyway)
InvokeDynamic
and MethodHandles
Requires registration via native-image
CLI/API
@RegisterForReflection
in Qarkus
Require registration via native-image
CLI/API
1/2
Build time OpenJDK instance:
2/2
Downsides:
no file handles, sockets, threads
Please!
$ mvn io.quarkus:quarkus-maven-plugin:0.12.0:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-hello \
-DclassName="org.acme.quickstart.GreetingResource" \
-Dpath="/hello"
$ mvn package
$ java -jar target/*-runner.jar
INFO [io.quarkus] (main) Quarkus 0.11.0 started in 0.729s.
INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
$ export GRAALVM_HOME=...
$ mvn clean package -Pnative
$ 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.11.0 started in 0.005s.
INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
$ mvn compile quarkus:dev
@QuarkusTest
runner
JUnit 4 or 5
$ mvn clean test
@SubstrateTest
runner
$ mvn clean verify -Pnative
https://quarkus.io/guides/building-native-image-guide.html#testing-the-native-executable
$ 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)
...
$ mvn quarkus:add-extension -Dextensions=hibernate-orm-panache
Makes simple Hibernate ORM easy
@Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birth;
public PersonStatus status;
}
// 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();
}
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();
Makes Java the choice #1 for the cloud and serverless