Closed yoshidaki closed 4 years ago
LINE公式アカウントとは企業/事業者向けに開設可能なLINEアカウントである。 ブロードキャストやターゲティング配信などの機能を有しており、一般ユーザ向けのチャットシステムとは異なる「LINEチャット」を利用してLINE公式アカウントと一般ユーザがチャットできる。
チャットシステムとして「LINEチャット」に求められるものは以下のものである。
リアルタイム通信の実現については、検討時期にはPolling、Long Polling、Server Sent Events(SSE)、WebSocketの4択となっていたが、SSEを採用することにした。 また、採用したSpring WebFluxはNon BlockingなWebフレームワークである。 1Request=1ThreadなServlet APIと異なり、1つのEvent Loop Treadで複数のRequestをさばけることから、サーバ台数を削減できる狙いがあった。
ユーザ操作起因のコアビジネスロジックの実現については、Spring WebMVCを採用した。 開発時期がSpring Boot 2.0.0.M1だったということもあったが、いきなりSpring WebFluxを全適用するのではなく、小さくて確実に向いているコンポーネントから試していこうという判断があった。
LINEユーザが送信した画像・動画・音声の取得については、Spring WebFluxを採用した。 画像・動画・音声ファイルは社内CDNにあり、認証等の事情によりアカウントオーナーにアクセスさせるにはProxyを掛ける必要があったが、Spring WebFluxのContent Proxyによりスレッドを占有することなくProxyを掛けることができた。
以下は開発時に困った点やノウハウのTips
WebFlux Clientを用いたコードでAPI callのtimeoutが大量に発生するとOutOfMemoryError(OOME)でサーバが落ちることがあった。 NettyのReadTimeoutExceptionがシングルトンであったため、ReadTimeoutExceptionが保持しているsuppressedExceptions(List)が肥大化してOOMEが発生した。 現在はIssueを起票して修正済である。
参考:
https://github.com/netty/netty/pull/9152
Reactor NettyではInetSocketAddressを使ったBlocking DNS Resolverを使用しているので、NettyのNon-Blocking DNS Resolverを使うように変更する必要がある。 また、ごくまれにTreadがハングしてしまう現象があったためIssueを起票済。(現在0.10.x向けBacklog) また、デフォルトをNon-Blocking DNS Resolverとする案もReactor Nettyに上がっている。
https://github.com/reactor/reactor-netty/issues/710 https://github.com/reactor/reactor-netty/issues/569
ServletのようにMDCにRequest情報を渡してログに出力させたかったがWebFluxのEvent Loop Threadと相性が悪かった。 WebFilter、Reactor Context、Hooksを活用して何とか実現した。
BlockHoundはNon BlockingスレッドにおけるBlockingコールを検出してくれるJavaエージェント。 あまり日本語で言及されている記事を見かけなかったので紹介。 GradleやMavenで依存関係に追加し、テストケースにてBlockHoundを有効化しておくと、テストケース実行時にBlockingコール発生部分でErrorを出力してくれる。 しばしば予期しないBlockingコールもあり、コードレビューで漏れることがあるので導入をお勧めする。
https://speakerdeck.com/line_developers/examples-of-using-spring-and-webflux-in-the-chat-system-for-line-official-accounts
[LINE公式アカウントのチャットシステムにおけるSpringおよびWebFluxの活用事例]
概要
このセッションに関連するキーワード
セッションの内容
話の流れ
LINE公式アカウントとは企業/事業者向けに開設可能なLINEアカウントである。 ブロードキャストやターゲティング配信などの機能を有しており、一般ユーザ向けのチャットシステムとは異なる「LINEチャット」を利用してLINE公式アカウントと一般ユーザがチャットできる。
チャットシステムとして「LINEチャット」に求められるものは以下のものである。
リアルタイム通信
リアルタイム通信の実現については、検討時期にはPolling、Long Polling、Server Sent Events(SSE)、WebSocketの4択となっていたが、SSEを採用することにした。 また、採用したSpring WebFluxはNon BlockingなWebフレームワークである。 1Request=1ThreadなServlet APIと異なり、1つのEvent Loop Treadで複数のRequestをさばけることから、サーバ台数を削減できる狙いがあった。
ユーザ操作起因のコアビジネスロジック
ユーザ操作起因のコアビジネスロジックの実現については、Spring WebMVCを採用した。 開発時期がSpring Boot 2.0.0.M1だったということもあったが、いきなりSpring WebFluxを全適用するのではなく、小さくて確実に向いているコンポーネントから試していこうという判断があった。
LINEユーザが送信した画像・動画・音声の取得
LINEユーザが送信した画像・動画・音声の取得については、Spring WebFluxを採用した。 画像・動画・音声ファイルは社内CDNにあり、認証等の事情によりアカウントオーナーにアクセスさせるにはProxyを掛ける必要があったが、Spring WebFluxのContent Proxyによりスレッドを占有することなくProxyを掛けることができた。
以下は開発時に困った点やノウハウのTips
Netty OOMEの発生(Nettyに報告・改修確認済)
WebFlux Clientを用いたコードでAPI callのtimeoutが大量に発生するとOutOfMemoryError(OOME)でサーバが落ちることがあった。 NettyのReadTimeoutExceptionがシングルトンであったため、ReadTimeoutExceptionが保持しているsuppressedExceptions(List)が肥大化してOOMEが発生した。
現在はIssueを起票して修正済である。
参考:
Blocking DNS Resolver
Reactor NettyではInetSocketAddressを使ったBlocking DNS Resolverを使用しているので、NettyのNon-Blocking DNS Resolverを使うように変更する必要がある。 また、ごくまれにTreadがハングしてしまう現象があったためIssueを起票済。(現在0.10.x向けBacklog) また、デフォルトをNon-Blocking DNS Resolverとする案もReactor Nettyに上がっている。
参考:
Reactor hooks with MDC
ServletのようにMDCにRequest情報を渡してログに出力させたかったがWebFluxのEvent Loop Threadと相性が悪かった。 WebFilter、Reactor Context、Hooksを活用して何とか実現した。
BlockHound
BlockHoundはNon BlockingスレッドにおけるBlockingコールを検出してくれるJavaエージェント。 あまり日本語で言及されている記事を見かけなかったので紹介。 GradleやMavenで依存関係に追加し、テストケースにてBlockHoundを有効化しておくと、テストケース実行時にBlockingコール発生部分でErrorを出力してくれる。 しばしば予期しないBlockingコールもあり、コードレビューで漏れることがあるので導入をお勧めする。
考察したこと
講義資料URL
https://speakerdeck.com/line_developers/examples-of-using-spring-and-webflux-in-the-chat-system-for-line-official-accounts