Edited at

MicronautでServerless Function


Micronaut Severless Function

Micronautには、Serverless Functionを作成するための機能があります。

Serverless Functions

Springなどとの他のフレームワークに比べての利点は、Micronautはコンパイル時に依存関係を解決する仕組みや、メモリのフットプリントの軽さが売りのようです。

作成したFunctionは、AWS LambdaやOpenFaaSなどにデプロイすることができるようです。

Deploying Functions

このMicronautのServerless Functionを作成する機能を、簡単に試してみたいと思います。


環境

環境は、こちらです。

$ mn -V

| Micronaut Version: 1.0.4
| JVM Version: 1.8.0_191


プロジェクト作成

--profilefunction-awsとして、プロジェクトを作成します。

$ mn create-app --profile function-aws --build maven hello-function

$ cd hello-function

指定できるfunctionのProfileは、function-awsのみのようなので…。

$ mn list-profiles

| Available Profiles
--------------------
cli The cli profile
federation The federation profile
function-aws The function profile for AWS Lambda
kafka The Kafka messaging profile
profile A profile for creating new Micronaut profiles
service The service profile

単にfunctionとか書いたりすると、「そんなProfileはない」と怒られます。

$ mn create-app --profile function --build maven hello-function

| Error Profile [function] is designed only for extension and not direct usage

できあがったプロジェクトには、テストコードなども含まれています。

$ find -type f

./mvnw.cmd
./.mvn/jvm.config
./.mvn/wrapper/maven-wrapper.properties
./.mvn/wrapper/MavenWrapperDownloader.java
./.mvn/wrapper/maven-wrapper.jar
./.gitignore
./mvnw
./pom.xml
./micronaut-cli.yml
./src/main/java/hello/function/HelloFunctionFunction.java
./src/main/java/hello/function/Application.java
./src/main/resources/application.yml
./src/main/resources/logback.xml
./src/test/java/hello/function/HelloFunctionFunctionTest.java
./src/test/java/hello/function/HelloFunctionClient.java

スケルトンのfunctionは、こんな感じです。

src/main/java/hello/function/HelloFunctionFunction.java

package hello.function;

import io.micronaut.function.FunctionBean;
import java.util.function.Supplier;

@FunctionBean("hello-function")
public class HelloFunctionFunction implements Supplier<String> {

@Override
public String get() {
return "hello-function";
}
}

functionを定義する方法は、FunctionBeanとして作成する方法と、Factoryを使って作成する方法があるようです。

FunctionBean

今回は、FunctionBeanを使った方法で実装されているというわけですね。

この場合、関数インターフェースを実装する必要があるようですが、利用できるインターフェースはSupplierConsumerBiConsumerFunctionBiFunctionのようです。

mainメソッドを持ったクラスは、こういう感じで。

src/main/java/hello/function/Application.java

package hello.function;

import io.micronaut.runtime.Micronaut;

public class Application {

public static void main(String[] args) {
Micronaut.run(Application.class);
}
}

実行するfunctionは、設定ファイルで指定されています。これは、FunctionBeanの名前ですね。

src/main/resources/application.yml

micronaut:

function:
name: hello-function

ドキュメントを見ると、複数のfunctionを含む場合は明示的に設定する必要があるみたいです。


CLIとして実行してみる

パッケージングして実行してみましょう。

Functions as CLI Applications

結果が返ってきました。

$ java -jar target/hello-function-0.1.jar 

12:56:07.817 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [function]
hello-function


引数を受け取ったり、他のBeanをDIしたりしてみる

ちょっと構成を変えて、引数を受け取ったり他のBeanをDIしたりしてみましょう。

引数のメッセージを加工するだけの、Beanを作成します。

src/main/java/hello/function/MessageService.java

package hello.function;

import java.time.LocalDateTime;
import javax.inject.Singleton;

@Singleton
public class MessageService {
public String wrap(String message) {
return String.format("[%s] ★%s★", LocalDateTime.now(), message);
}
}

もとのfunctionは、このBeanをDIしつつ、実行時に引数を受け取るようにしてみましょう。関数インターフェースは、Functionを実装しました。

src/main/java/hello/function/HelloFunctionFunction.java

package hello.function;

import io.micronaut.function.FunctionBean;

import java.util.function.Function;
import java.util.function.Supplier;
import javax.inject.Inject;

@FunctionBean("hello-function")
public class HelloFunctionFunction implements Function<HelloFunctionFunction.Request, HelloFunctionFunction.Response> {
@Inject
MessageService messageService;

@Override
public Response apply(Request request) {
String replyMessage = messageService.wrap(request.getMessage());

return new Response(replyMessage);
}

public static class Request {
String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

public static class Response {
String message;

public Response(String message) {
this.message = message;
}

public String getMessage() {
return message;
}
}
}

コードが変わってしまったので、今回はテストをスキップしてパッケージング。

$ ./mvnw package -DskipTests=true

実行。

$ echo '{"message": "こんにちは、世界"}' | java -jar target/hello-function-0.1.jar 

13:00:23.558 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [function]
{"message":"[2019-03-05T13:00:24.185] ★こんにちは、世界★"}

引数を受け取りつつ、他のBeanをDIして実行できました。

functionをWebアプリケーションとしても実行できるようですが、今回はパスします。

実行環境へのデプロイは、そのうち…。