軽量フレームワークMicronautでサーバーレスJavaを始めよう！

サーバーレスがとても盛り上がっていますね。
Javaはいわゆるコールドスタートの問題でサーバーレスには向いていないとの声もありましたが、今回紹介するMicronautを始めとしたマイクロフレームワークや、GraalVMのネイティブイメージ作成機能(SubstrateVM)の登場により状況が大きく変わってきています。

この辺りの流れは、@_kenshさんや@jyukutyoさんの「JJUG CCC 2019 Fall」での素晴らしい講演資料がありますので、そちらを見てワクワクしましょう。

- [Serverless時代のJavaについて](https://www.slideshare.net/AmazonWebServicesJapan/serverlessjava-199195000)
- [多言語対応の仮想マシンGraalVMが照らす未来](https://www.slideshare.net/jyukutyo/guide-to-graalvm-jjug-ccc-2019-fall)

さあ、ではMicronautで実際にサーバーレスJavaを作ってみましょう！

## なんでMicronaut？
Micronautは、キャッチコピーの中で「サーバーレスのためのフレームワーク」であることを謳っています。
また「re:Invent2019」にて、MicronautとLambdaについてのセッションも開催していました。
他のフレームワークと比較して特にサーバーレスに力を入れていて、個人的にとても魅力を感じたので今回セレクトしました。

## 他のフレームワークはないの？
Java(を始めとするJVM言語)が使用できるマイクロフレームワークとしてメジャーなのは、下記3つかと思います。

* **[Micronaut](https://micronaut.io/index.html)**
* **[Quarkus](https://quarkus.io/)**
* **[Helidon](https://helidon.io/#/)**

どれもこれからが楽しみなフレームワークですね。
キャッチコピーなどを比較してみたので、ご参考までにどうぞ。

#### キャッチコピー
|フレームワーク|キャッチコピー|意訳|
|:-:|:--|:--|
|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年間）
![Screen Shot 2019-12-04 at 23.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500976/b771d691-5b8c-426d-e276-8a95f1bb3160.png)
  
#### GitHubスター数

![Screen Shot 2019-12-04 at 22.59.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500976/59c9fc9e-6dfa-33f0-c8ea-794fb8177e6e.png)
[https://www.githubcompare.com/](https://www.githubcompare.com/micronaut-projects/micronaut-core+quarkusio/quarkus+oracle/helidon)

#### その他
- 2019/11/28にリリースされたIntelliJ IDEA 2019.3では、「Microservices frameworks support」としてMicronaut、Quarkus、Helidonの3つがサポートされました。注目度合いが伺えますね！
- Spring Frameworkも次期バージョンの5.3でGraalVMに対応するようです。こちらも楽しみ！

## Micronautのセットアップ

では、Micronautをセットアップしてみましょう。
[公式サイト](https://micronaut.io/download.html)では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の設定値を返すだけの処理になっています。

```java:HelloWorldFunction.java
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」を設定。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500976/b6939a8f-da73-5218-ed4e-2d7eb9918e46.png)

## Lambda関数の実行

それではLambda関数を実行してみましょう。
Lambdaコンソール上部の「Test」ボタンから、リクエストのJSONを設定します。
JSONには下記を指定。

```json
{
  "name": "Lambda"
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500976/89ec6c57-60bd-86f6-0241-0312d1327cfe.png)

「Test」実行！
動きましたね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500976/5259de5d-5def-0695-d904-1e4637b88227.png)

## まとめ

今回は単純な処理の実行だけでしたが、気軽にフレームワークを含んだLambda関数を作成して実行できることがわかったと思います。
Micronautなどの軽量フレームワークを使うことで、フレームワークの豊富な機能を使いながらLambdaを構築することができるようになるため、大規模アプリケーションへのサーバーレスアーキテクチャの適用もますます拡がっていきそうですね。

一番の懸念だったコールドスタートについても、先日のre:Invent2019で発表された「provisioned concurrency」という有力な解決策が提示されたため、本番環境への適用に対するハードルもどんどん下がってきています。


2020年もさらなる拡がりが予想されるサーバーレスの世界、とても楽しみですね！
特にJavaの大規模システムに関わっていてサーバーレスにあまり馴染みがなかったという方も、今が始めどきだと思います。
一緒にサーバーレスJavaを始めましょう！

## 参考
[Java最新フレームワーク、Helidon、Micronaut、Quarkusをnative-imageまでまとめて試す](https://nowokay.hatenablog.com/entry/2019/07/15/093418)
[Micronaut User Guide](https://docs.micronaut.io/latest/guide/index.html#serverlessFunctions)
[AWS Lambda announces Provisioned Concurrency](https://aws.amazon.com/about-aws/whats-new/2019/12/aws-lambda-announces-provisioned-concurrency/)