Java最新フレームワーク、Helidon、Micronaut、Quarkusをnative-imageまでまとめて試す

最近でてきたフレームワーク、Helidon、Micronaut、Quarkusのクイックスタート、Native-Imageをまとめて試しましょう。

準備

SDKMANインストール

今回はSDKMANで環境を作っていきます。
https://sdkman.io/

コマンドラインで次のコマンドを実行します。Windowsの場合はCygwinかWSLで。

$ curl -s "https://get.sdkman.io" | bash

ターミナルを開きなおすか次のコマンドを実行するとSDKMANが有効になります。

$ source "$HOME/.sdkman/bin/sdkman-init.sh"

JDKインストール

今回はnative-imageまで使うのでGraalVMを使っておきましょう。

$ sdk use java 19.1.0-grl

native-imageの準備も行います。Cygwinではnative-imageはバンドルされているので不要ですが、いまのところ3つのフレームワークのネイティブ化ができてません。

$ gu install native-image

Mavenインストール

HelidonとQuarkusではMavenを使うのでインストールしておきます。

$ sdk install maven

Helidon

HelidonはOracleWebLogicチームが開発しているフレームワークです。
https://helidon.io/

独自APIを使うSEとMicroProfileを使うMPがありますが、現時点でnative-imageに対応しているのはSEのみなので、ここではSEを使います。

プロジェクト作成

archetypeから作成しますが、いろいろ入力するのは面倒なので、候補から選択します。

$ mvn archetype:generate

たくさん候補がでるので、絞り込みます。

...
2450: remote -> xyz.luan.generator:xyz-gae-generator (-)
2451: remote -> xyz.luan.generator:xyz-generator (-)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1387:

ここでhelidonと入力すると2つに絞られます。

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1387: helidon
Choose archetype:
1: remote -> io.helidon.archetypes:helidon-quickstart-mp (Quickstart archetype for Helidon MP)
2: remote -> io.helidon.archetypes:helidon-quickstart-se (Quickstart archetype for Helidon SE)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): :

2を入力してHelidon SEを選びます。

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 2
Choose io.helidon.archetypes:helidon-quickstart-se version:
1: 0.9.0
...
16: 1.1.1
17: 1.1.2
Choose a number: 17:

そのままenterをおして最新を選びます。
その後、groupIdartifactIdversionpackageを入力します。groupIdは組織名、artifactIdがプロジェクト名です。

Define value for property 'groupId': kis
Define value for property 'artifactId': start-helidon
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' kis: :
Confirm properties configuration:
groupId: kis
artifactId: start-helidon
version: 1.0-SNAPSHOT
package: kis
 Y: : y

artifactIdと同じ名前のディレクトリができているので移動しておきます。

$ cd start-helidon

ビルド

ビルドはMavenで。

$ mvn package

実行

targetにartifactIdと同じ名前のjarファイルができていて、このjarファイルを実行すればサーバーが起動します。

$ java -jar target/start-helidon.jar
[DEBUG] (main) Using Console logging
2019.07.15 07:35:48 INFO io.helidon.webserver.NettyWebServer Thread[main,5,main]: Version: 1.1.2
2019.07.15 07:35:48 INFO io.helidon.webserver.NettyWebServer Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default' started: [id: 0x8985c83f, L:/0:0:0:0:0:0:0:0:8080]
WEB server is up! http://localhost:8080/greet

メッセージどおりhttp://localhost:8080/greetにアクセスするとJSONが返ってきます。
f:id:nowokay:20190715073853p:plain

ネイティブ化

native-imageプロファイルでMavenビルドするとネイティブ化できます。

$ mvn -Pnative-image package

1分ほど待つと、targetディレクトリに実行ファイルができます。

$ target/start-helidon
2019.07.15 07:42:14 INFO io.helidon.webserver.NettyWebServer !thread!: Version: 1.1.2
2019.07.15 07:42:14 INFO io.helidon.webserver.NettyWebServer !thread!: Channel '@default' started: [id: 0xb59ae091, L:/0:0:0:0:0:0:0:0:8080]
WEB server is up! http://localhost:8080/greet

Micronaut

MicroanutはGrailsを作っていたObject Computing社が開発しているフレームワークです。独自APIを使っていて、APIの使いやすさやドキュメントのわかりやすさが魅力。KotlinやGroovyにも対応しています。
https://micronaut.io/

プロジェクト作成

プロジェクト作成にはMicornautのコマンドを使います。これもSDKMANでインストールできます。

$ sdk install micronaut

mnコマンドでプロジェクトを生成します。

$ mn create-app start-mn
Resolving dependencies..
| Generating Java project...
| Application created at /home/naoki/starts/start-mn

プロジェクト名のディレクトリに移動します。

$ cd start-mn

コントローラーは作成されないので、mnコマンドでコントローラーを作成します。

$ mn create-controller MyController

src/main/java/start/mn/MyController.javaが作成されるので少し編集します。

package start.mn;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller // パスを消しておく
public class MyController {
  @Get(produces="TEXT/PLAIN") // 出力メディアタイプを指定
  public String hello() { // 戻り値をStringに
    return "Hello"; // メッセージを返す
  }
}

ビルド

ビルドはGradleを使います。

$ ./gradlew build

> Task :compileJava
Note: Creating bean classes for 1 type elements

BUILD SUCCESSFUL in 8s
10 actionable tasks: 10 executed

実行

build/libsに実行可能jarが作成されているので実行するとサーバーが起動します。

$ java -jar build/libs/start-mn-0.1-all.jar
07:57:32.751 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 1496ms. Server Running: http://localhost:8080

http://localhost:8080にアクセスするとメッセージが表示されます。
f:id:nowokay:20190715075901p:plain

※ 8/13追記 1.2からstart-mn-0.1.jarじゃなくstart-mn-0.1-all.jarになった?

ネイティブ化

Micronautでは特別なネイティブ化コマンドは用意されていないので、native-imageコマンドを直接使います。

$ native-image -jar build/libs/start-mn-0.1-all.jar

カレントディレクトリに実行ファイルが作成されます。

$ ./start-mn-0.1-all
08:03:09.159 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 131ms. Server Running: http://localhost:8080

Quarkus

QuarkusはRed Hatが開発しているフレームワークで、MicroProfileをベースにしています。
https://quarkus.io/

プロジェクト作成

プロジェクト作成にはMavenを使いますが、通常のarchetypeではなく独自プラグインを使います。

$ mvn io.quarkus:quarkus-maven-plugin::create

groupIdartifactIdのほかにRESTリソースを作るかきかれるので、yにしておきます。

$ mvn io.quarkus:quarkus-maven-plugin::create
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- quarkus-maven-plugin:0.19.1:create (default-cli) @ standalone-pom ---
Set the project groupId [org.acme.quarkus.sample]: kis
Set the project artifactId [my-quarkus-project]: start-quarkus
Set the project version [1.0-SNAPSHOT]:
Do you want to create a REST resource? (y/n) [no]: y
Set the resource classname [kis.HelloResource]:
Set the resource path  [/hello]:
Creating a new project in /home/naoki/starts/start-quarkus
...

ビルド

ビルドはMavenでいつもどおり

$ mvn package

実行

targetディレクトリに実行ファイルができるので、javaコマンドで実行することができます。

$ java -jar target/start-quarkus-1.0-SNAPSHOT-runner.jar
2019-07-15 08:25:07,637 INFO  [io.quarkus] (main) Quarkus 0.19.1 started in 0.878s. Listening on: http://[::]:8080
2019-07-15 08:25:07,656 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

http://localhost:8080/hello にアクセスするとメッセージが表示されます。 f:id:nowokay:20190715082238p:plain

Quarkusの場合、Mavenで実行することでオートリローディングが有効になるので、開発時はこちらを使うほうが便利です。

$ mvn quarkus:dev

ネイティブ化

Quarkusのネイティブ化は、nativeプロファイルを指定してビルドします。

$ mvn -Pnative package

1分ちょい待つとtargetディレクトリに実行ファイルができるので、これを実行するとサーバーが起動します。

$ target/start-quarkus-1.0-SNAPSHOT-runner
2019-07-15 08:31:53,378 INFO  [io.quarkus] (main) Quarkus 0.19.1 started in 0.016s. Listening on: http://[::]:8080
2019-07-15 08:31:53,379 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

起動時間やメモリの比較

今回はWSL上のUbuntuでの起動時間と利用メモリを比較してみます。
Helidonでは起動時間が表示されていないので、次のような表示コードを追加しておきます。

System.out.println(ManagementFactory.getRuntimeMXBean().getUptime());

起動時間は次のようになります。

GraalVM JDK OpenJDK 12.0.1 native-image
Helidon 864ms 715ms 16ms
Micronaut 1552ms 1387ms 109ms
Quarkus 819ms 720ms 15ms

f:id:nowokay:20190715093023p:plain
ネイティブ化すると圧倒的に起動が速くなってますが、GraalVMよりもOpenJDK 12のほうがかなり速いですね。GraalとC2の違いだと思います。GraalではJavaで書かれているGraal自体をJITするのに時間がかかるので起動は遅くなりがちです。

メモリ使用量は次のような感じに。

GraalVM JDK OpenJDK 12.0.1 native-image
Helidon 233.7MB 64.9MB 7.4MB
Micronaut 344.6MB 95.2MB 15.0MB
Quarkus 246.6MB 81.3MB 6.9MB

f:id:nowokay:20190715092812p:plain

これもネイティブ化するとメモリ使用量が激減してますが、GraalVMよりもOpenJDK 12のほうが少なくなってます。Graalがメモリを使ってるんですかね。