Spring WebfluxとKotlinCoroutine

こちら、[Kotlinアドベントカレンダー](https://qiita.com/advent-calendar/2019/kotlin) 18日目の記事です。
こんばんはこんにちは、今年は様々な事がありました。東京で暮らし始めたSatohJohnです。
今回は社内開発で利用したKotlinでのSpring WebFlux（主にrouter）の書き方についてです。
Spring WebFlux自体については、ググれば色々調べれば出てくると思います！！素晴らしい！！

## 環境
||Gradle(pluginId or artifactId)|バージョン|
|---|---|---|
|Spring Boot|org.springframework.boot|2.2.2.RELEASE|
|kotlin|org.jetbrains.kotlin.jvm<br>org.jetbrains.kotlin.plugin.spring|1.3.61|
|kotlinx| kotlinx-coroutines-core<br>kotlinx-coroutines-reactor|1.3.3|

## 書き方
### JavaでのSpring WebfluxのRouter
routerに対して `Mono<ServerResponse>` を返却する形になります。

```Java
@Bean
public RouterFunction<ServerResponse> routes() {
    return RouterFunctions
            .route(GET("/hello"), req ->
                    ServerResponse.ok().bodyValue(String.format("hello world. %s", req.queryParam("name").orElse("anonymous"))));
}
```

悪くないですね！！ラムダ式周りで結構書きやすいです

### KotlinでのSpring WebfluxのRouter

```Kotlin
@Bean
fun routes() = router {
    GET("/hello") {
        ServerResponse.ok().bodyValue("hello world, ${it.queryParam("name").orElse("anonymous")}")
    }
}
```
Kotlinの場合はdslで書けるので、少しシンプルに見えますね！

### KotlinでのSpring WebfluxのRouter(Coroutineを使うパターン)
`CoRouterFunctionDsl.GET` が `suspend` の関数で `ServerResponse` を返却することを求めていますので、 `ServerResponse.bodyValueAndAwait` を利用します。

```Kotlin
@Bean
fun routes() = coRouter {
    GET("/hello") {
        ServerResponse.ok().bodyValueAndAwait("hello world, ${it.queryParam("name").orElse("anonymous")}")
    }
}
```

なんとなくKotlinのDSLが読みやすいのはわかりますね。

## 複数の要素を返す場合

上記は要素一つを返す例でした。次は要素を複数返す場合についてです。

### Javaでの場合
Fluxっぽく無限stream要素をから、1秒ごとに一つづつ返すものとします
JavaではMonoではなくFluxを返すことになります（bodyValueがMonoを返していた)

```Java
@Bean
public RouterFunction<ServerResponse> routes() {
    return RouterFunctions
            .route(GET("/hello"), req -> 
                    ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_STREAM_JSON)
                            .body(
                                    Flux.fromStream(Stream.iterate(0, i -> i + 1))
                                            .delayElements(Duration.ofSeconds(1)),
                                    Integer.class
                            )
            );
}
```

### Kotlinでの場合
KotlinではFlowを返します。
基本はListからFlowにします(無限ストリームを利用するのでsequenceから作成しています

```Kotlin
@Bean
fun routes() = coRouter {
    GET("/hello") {
        ServerResponse.ok()
            .contentType(MediaType.APPLICATION_STREAM_JSON)
            .bodyAndAwait(flow {
                generateSequence(0) { it + 1 }.asFlow().collect {
                    delay(1000)
                    emit(it)
                }
            })
    }
}
```

リクエストを投げるとJavaのときと同じようにが返却されているのがわかります。

```
curl -v localhost:8080/hello -H 'Accept: application/stream+json'
```

```
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /hellok HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: application/stream+json
> 
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: application/stream+json
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
< Referrer-Policy: no-referrer
< 
0
1
2
3
4
5
6
7
```

## 考察
はてさて、Kotlinを使うということDSLだと短くきれいに書けますね。っていうのは良いと思います
ではcoroutineを使う必要はあるんでしょうか？~~使いたいんだから使うんだよという気持ち~~

個人的には、MonoやFluxの(reactor-core)に依存せず、Kotlinの言語にあるcoroutine(suspendやFlow)に依存するところの違いかなぁと思っています。
bodyValueAndAwaitやbodyAndAwaitは内部で `reactive-stream` には依存していますが、`reactor-core` というライブラリの実装に依存していない感じです。

詳しくは以下の通り書いてくださっているので、こちらも合わせて参照してもらえると良いのかなぁと思います
https://github.com/pljp/kotlinx.coroutines/blob/japanese_translation/reactive/coroutines-guide-reactive.md

## まとめ
Kotlin書きやすいですね、読みやすいですし。
参考資料などはあまりなく、開発時は色々辛いこと（例外がどこで起きたのか全然追えないなど）もありましたが
現在はうまく使えていますし、また今回WebFluxと合わせることで、Coroutineのことが若干わかった気がしました。