サーバーレスがとても盛り上がっていますね。
Javaはいわゆるコールドスタートの問題でサーバーレスには向いていないとの声もありましたが、今回紹介するMicronautを始めとしたマイクロフレームワークや、GraalVMのネイティブイメージ作成機能(SubstrateVM)の登場により状況が大きく変わってきています。
この辺りの流れは、@_kenshさんや@jyukutyoさんの「JJUG CCC 2019 Fall」での素晴らしい講演資料がありますので、そちらを見てワクワクしましょう。
さあ、ではMicronautで実際にサーバーレスJavaを作ってみましょう!
なんでMicronaut?
Micronautは、キャッチコピーの中で「サーバーレスのためのフレームワーク」であることを謳っています。
また「re:Invent2019」にて、MicronautとLambdaについてのセッションも開催していました。
他のフレームワークと比較して特にサーバーレスに力を入れていて、個人的にとても魅力を感じたので今回セレクトしました。
他のフレームワークはないの?
Java(を始めとするJVM言語)が使用できるマイクロフレームワークとしてメジャーなのは、下記3つかと思います。
どれもこれからが楽しみなフレームワークですね。
キャッチコピーなどを比較してみたので、ご参考までにどうぞ。
キャッチコピー
フレームワーク | キャッチコピー | 意訳 |
---|---|---|
Micronaut | A modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications. | マイクロサービスとサーバーレスのためのモダンなフルスタックフレームワークだよ |
Quarkus | A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM. | OpenJDK HotSpotとGraalVMに最適化したKubernetesネイティブなフレームワークだよ |
Helidon | Lightweigt. Fast. Crafted for Microservices. | マイクロサービスのために作られた軽量で高速なフレームワークだよ |
Googleトレンド(アメリカ、過去1年間)
GitHubスター数
https://www.githubcompare.com/
その他
- 2019/11/28にリリースされたIntelliJ IDEA 2019.3では、「Microservices frameworks support」としてMicronaut、Quarkus、Helidonの3つがサポートされました。注目度合いが伺えますね!
- Spring Frameworkも次期バージョンの5.3でGraalVMに対応するようです。こちらも楽しみ!
Micronautのセットアップ
では、Micronautをセットアップしてみましょう。
公式サイトではSDKMANでのインストールが推奨されているので、その手順でインストールしましょう。
SDKMANのインストール
$ curl -s https://get.sdkman.io | bash
ターミナル再起動、もしくは下記シェルを実行
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
sdkコマンドが通ればOK
$ sdk version
SDKMAN 5.7.4+362
Micronautのインストール
$ sdk install micronaut
mnコマンドが通ればOK
$ mn -V
| Micronaut Version: 1.2.7
| JVM Version: 11.0.5
MicronautでのLambda関数の作成
プロジェクトの雛形はmnコマンド経由で作成します。
mnだけを入力すると対話モードで起動できます。
コマンド補完もできるので便利です。コマンド履歴も辿れます。
$ mn
| Starting interactive mode...
| Enter a command name to run. Use TAB for completion:
mn>
コマンドの使い方はmn> help
で確認できます。
mn> help
Usage: mn [-hnvVx] [COMMAND]
Micronaut CLI command line interface for generating projects and services.
Commonly used commands are:
create-app NAME
create-cli-app NAME
create-federation NAME --services SERVICE_NAME[,SERVICE_NAME]...
create-function NAME
・・・
今回はLambda関数を作りたいので、create-function
を使いましょう。
mn> create-function hello-world
| Generating Java project...
| Function created at /Users/cojohnny/micronaut/hello-world
| Initializing application. Please wait...
一度対話モードを抜けて、生成されたプロジェクトを確認します。
mn> exit
$ cd hello-world/
$ tree
.
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── micronaut-cli.yml
├── settings.gradle
└── src
├── main
│ ├── java
│ │ └── hello
│ │ └── world
│ │ ├── Application.java
│ │ ├── HelloWorld.java
│ │ └── HelloWorldFunction.java
│ └── resources
│ ├── application.yml
│ └── logback.xml
└── test
└── java
└── hello
└── world
├── HelloWorldClient.java
└── HelloWorldFunctionTest.java
生成されたソースを見てみましょう。
HelloWorldFunction.javaのapplyメソッドがLambda関数です。
今の実装は、入力されたJSONのnameの設定値を返すだけの処理になっています。
package hello.world;
import io.micronaut.function.executor.FunctionInitializer;
import io.micronaut.function.FunctionBean;
import javax.inject.*;
import java.io.IOException;
import java.util.function.Function;
@FunctionBean("hello-world")
public class HelloWorldFunction extends FunctionInitializer implements Function<HelloWorld, HelloWorld> {
@Override
public HelloWorld apply(HelloWorld msg) {
return msg;
}
/**
* This main method allows running the function as a CLI application using: echo '{}' | java -jar function.jar
* where the argument to echo is the JSON to be parsed.
*/
public static void main(String...args) throws IOException {
HelloWorldFunction function = new HelloWorldFunction();
function.run(args, (context)-> function.apply(context.get(HelloWorld.class)));
}
}
ではgradlew
でビルドしてみましょう。
$ ./gradlew assemble
Jarが2種類出来上がります。
$ tree build/libs/
build/libs/
├── hello-world-0.1-all.jar
└── hello-world-0.1.jar
ファイル名の末尾に-all
とついているほうが、依存ライブラリが含まれた「Fat Jar」なので、そちらをLambdaにアップロードします。
Lambda関数のセットアップ
作成した「hello-world-0.1-all.jar」をAWSにアップロードしましょう。
Handlerには「hello.world.HelloWorldFunction::apply」を設定。
Lambda関数の実行
それではLambda関数を実行してみましょう。
Lambdaコンソール上部の「Test」ボタンから、リクエストのJSONを設定します。
JSONには下記を指定。
{
"name": "Lambda"
}
「Test」実行!
動きましたね!
まとめ
今回は単純な処理の実行だけでしたが、気軽にフレームワークを含んだLambda関数を作成して実行できることがわかったと思います。
Micronautなどの軽量フレームワークを使うことで、フレームワークの豊富な機能を使いながらLambdaを構築することができるようになるため、大規模アプリケーションへのサーバーレスアーキテクチャの適用もますます拡がっていきそうですね。
一番の懸念だったコールドスタートについても、先日のre:Invent2019で発表された「provisioned concurrency」という有力な解決策が提示されたため、本番環境への適用に対するハードルもどんどん下がってきています。
2020年もさらなる拡がりが予想されるサーバーレスの世界、とても楽しみですね!
特にJavaの大規模システムに関わっていてサーバーレスにあまり馴染みがなかったという方も、今が始めどきだと思います。
一緒にサーバーレスJavaを始めましょう!
参考
Java最新フレームワーク、Helidon、Micronaut、Quarkusをnative-imageまでまとめて試す
Micronaut User Guide
AWS Lambda announces Provisioned Concurrency