Java
Micronaut

MicronautでReactiveなサーバーを書く


Micronaut Reactive HTTP Request Processing

Micronautは、Netty上に構築されているため、Non-BlockingなI/Oを行うことも可能だとか。

Reactive HTTP Request Processing


If your controller method returns a non-blocking type such as an RxJava Observable or a CompletableFuture then Micronaut will use the Event loop thread to subscribe to the result.


Controllerのメソッドが、RxJavaのObservableCompletableFutureを返すとEventループスレッドを使って、Controllerの結果をサブスクライブします、と。


If however you return any other type then Micronaut will execute your @Controller method in a preconfigured I/O thread pool.


それ以外の型を返す場合は、I/Oスレッドプールを使用するそうです。

Controllerの戻り値の型が大事なんですねぇ。

まあ、簡単にですが、使ってみましょう。


環境

今回の環境は、こちら。

$ mn -V

| Micronaut Version: 1.0.4
| JVM Version: 1.8.0_191


サンプルアプリケーション

とりあえず、ひな型のプロジェクトを作成します。

$ mn create-app hello-reactive --build maven

$ cd hello-reactive

mainメソッドを持ったクラスは、とりあえずそのままにして

src/main/java/hello/reactive/Application.java

package hello.reactive;

import io.micronaut.runtime.Micronaut;

public class Application {

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

Controllerを作ってみます。

src/main/java/hello/reactive/HelloReactiveController.java

package hello.reactive;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.reactivex.Flowable;
import org.reactivestreams.Publisher;

@Controller("/reactive")
public class HelloReactiveController {

}

メソッドの中身を書いていきましょう。

まずは、どんな型を返せばよいのか、ドキュメントで確認してみます。

Reactive Responses



  • org.reactivestreams.Publisher


    • RxJavaのSingleObservable

    • ReactorのMonoFlux



  • java.util.concurrent.CompletableFuture

という感じみたいです。

なお、メソッドの引数についてもCompletableFutureやReactiveな型を使ってよいみたいです。

1秒おきに、メッセージを返すtext/event-streamなメソッドを作ってみます。

    @Get(value = "/hello", produces = MediaType.TEXT_EVENT_STREAM)

public Publisher<String> hello() {
return Flowable
.fromArray("Hello Reactive")
.repeat(10)
.delay(1, TimeUnit.SECONDS)
.map(m -> String.format("[%s] %s", LocalDateTime.now(), m));
}

作成されたプロジェクトの依存関係を調べると、RxJavaがあったのでこちらを利用。

$ ./mvnw dependency:tree

[INFO] +- io.micronaut:micronaut-runtime:jar:1.0.4:compile
[INFO] | +- io.micronaut:micronaut-aop:jar:1.0.4:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.8:compile
[INFO] | | \- com.fasterxml.jackson.core:jackson-core:jar:2.9.8:compile
[INFO] | +- io.reactivex.rxjava2:rxjava:jar:2.2.2:compile

起動させて、確認。

$ curl -i localhost:8080/reactive/hello

HTTP/1.1 200 OK
transfer-encoding: chunked
Date: Wed, 20 Feb 2019 13:00:07 GMT
transfer-encoding: chunked
content-type: text/event-stream

data: [2019-02-20T13:00:08.861] Hello Reactive

data: [2019-02-20T13:00:09.940] Hello Reactive

data: [2019-02-20T13:00:10.943] Hello Reactive

data: [2019-02-20T13:00:11.946] Hello Reactive

data: [2019-02-20T13:00:12.948] Hello Reactive

data: [2019-02-20T13:00:13.950] Hello Reactive

data: [2019-02-20T13:00:14.953] Hello Reactive

data: [2019-02-20T13:00:15.954] Hello Reactive

data: [2019-02-20T13:00:16.956] Hello Reactive

data: [2019-02-20T13:00:17.959] Hello Reactive

なんか、それっぽく動いています。

引数を受け取るものも書いてみましょう。

    @Post(value = "echo", consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_EVENT_STREAM)

public Publisher<String> echo(@Body Flowable<String> text) {
return text.map(t -> "★" + t + "★");
}

入力もストリームになっているわけではありませんが…。

$ curl -i -XPOST -H 'Content-Type: text/plain' localhost:8080/reactive/echo -d 'hello'

HTTP/1.1 200 OK
transfer-encoding: chunked
Date: Wed, 20 Feb 2019 13:01:29 GMT
transfer-encoding: chunked
content-type: text/event-stream

data: hello

これ、依存関係にReactorとか入れてもいいんでしょうかね?

とりあえず、簡単な確認はできましたと。