twitter Finagleのデータの流れ

[Finagle](https://twitter.github.io/finagle/) についてChannel ~ Service部分のデータの流れが気になったので調べた
Finagleについては既知かもしれないがNettyのラッパーとして作られているサーバフレームワークだ、一般的にはWebサーバフレームワークとして使われている

`com.twitter.finagle.Http.serve` をエントリポイントとしてServerの立ち上げ(のNettyServer構成部分)、そしてリクエストの受け入れ〜レスポンスのデータの流れを追っていこう

## `com.twitter.finagle.Http.serve`
名前の通りサーバアプリケーションを立ち上げるメソッドだ
overrideで様々な定義がされているが今回は [QuickStart](https://twitter.github.io/finagle/guide/Quickstart.html) 同様 `com.twitter.finagle.Server[Req, Rep]#serve(addr: String, service: Service[Req, Rep])` で起動したとして進めていこう

型パラメタ `[Req, Rep]` はfinagle Httpサーバの場合は `[Request, Response]` 型となる
第二引数 `Service` はユーザ側で定義するリクエストのハンドリング処理となる、今回話がしたい部分ではないので割愛する
(今回の話の上ではただの `Request => Future[Response]` 型の関数として認識してもらっても問題ない、たぶん)
`serve` 引数値を加工したのち、 `StdStackServer` インスタンスの作成 & `StdStackServer#serve` メソッドの呼び出しを行う
`StdStackServer#serve` ではHttp2の判定を行ったあと
`ListeningStackServer#serve` を呼び出す 
(`Stack.Params` については正直良くわかっていないので割愛する)

## `com.twitter.finagle.server.ListeningStackServer#serve`
`ListeningServer` インスタンスを作成している
コンストラクタに様々な処理が含まれているが、今回の話で着目してもしいのが `newListeningServer` の呼び出しだ、この中で `StdStackServer#newListener().listen` が呼び出されている

## `com.twitter.finagle.server.StdStackServer#newListener`
実装は `params[HttpImpl].listener(params)` の一行だ
`params[HttpImpl]` からは `com.twitter.finagle.Http.Netty4Impl` が取得され(おそらくこれはデフォルト値だ) `listener` を呼び出すことで `com.twitter.finagle.netty4.Netty4Listener`
 のインスタンスが取得される
ちなみに `Netty4Listener` の第三引数はidentity、第四引数は `ChannelTransport` が使用される

## `com.twitter.finagle.netty4.Netty4Listener.listen`
`ListeningServerBuilder#bindWithBridge` を呼び出している netty `ServerBootstrap` の組み上げとNetty<->Finagle Service間の橋渡し部分の構築を行う

## `com.twitter.finagle.netty4.ListeningServerBuilder#bindWithBridge`
ここがNettyとの境界を理解するためにおそらく一番重要な箇所だ
BossGroupとWorkGroupにはLinux専用の機能を使うかのフラグを見た後 `NioEventLoopGroup` か `EpollEventLoopGroup` が使用される(後者がLinux専用のものだ)
BossGroupのほうはThread数が1で固定されているWorkGroupのほうはデフォルトでは8以上だ(多くない?)
他様々なoptionが設定されたあと、Handlerの登録に入る
順番はコメントにも書かれている通り `raw => marshalling => framed => bridge` の順で適用される(コメントにはないが実際には `pipelineInit` も適用される)
あっているか自信はないがひとつずつ見ていこう

### pipelineInit
長い…
`com.twitter.finagle.netty4.http.ServerPipelineInit` より `HttpServerCodec` と `initServer` メソッド内の各ハンドラが追加される
netty側で用意されているhttp系のハンドラなので割愛

### Netty4RawServerChannelInitializer(raw) 
- Netty4ServerSslChannelInitializer  
コメントに書かれている通りSSL/TLS用のハンドラ群だ、中身については追わない
- ChannelStatsHandler  
複数のチャネルにまたがって計測する値などを管理するハンドラだと思う
- ChannelSnooper  
多分logger的なやつ…

### marshalling
今回の例ではidentityが渡ってきているので、このイニシャライザではとくに何もハンドラを追加しない

### Netty4FramedServerChannelInitializer(framed)
- writeTimeout/readTimeout  
タイムアウトのハンドラ
- ChannelRequestStatsHandler  
requestCountするハンドラらしい

### ServerBridge(bridge)
Nettyの世界からFinagleの世界への橋渡しを行うレイヤ、今回の話のキモ的な部分
`ServerBridge` の実装自体は簡単で自信をpipelineから排除したのち、transporterの作成+コールバックの実行のみを行っている
つまり本格的な受けたしの実装はTransporter及びコールバックのほうとなる、詳しく見ていこう
(コールバックはlithen呼び出し側の [この部分](https://github.com/twitter/finagle/blob/dc4bfbcf226adc7b1a5f52366148f8ac36690a71/finagle-core/src/main/scala/com/twitter/finagle/server/StdStackServer.scala#L82-L100) だ)

## Transporter/Dispatcher
Transporterコールバック関数にてStreamTransporter/Dispatcherでラップされる
それを踏まえた上でChannel ~ Dispatcherまでのデータの流れを追っていこう

### ChannelTransport
コンストラクタ内で `ChannelInboundHandlerAdapter` を生成している
その中の `channelRead` 関数で自身が持っている(あとで説明するが `Dispatcher` からreadされる)QueueにMsgを詰めている、NettyのChannelの概念は以降登場しない、これ以降はFinagleの世界だ

### GenStreamingSerialServerDispatcher
内部的に処理loopが回っており、そこで `Transporter` のQueueからデータを読み出すread関数を読み出している
気をつけたいのが呼び出されているのは `ChannelTransport#read` ではなく `Netty4ServerStreamTransport#read` だ(結局 `Netty4ServerStreamTransport#read` から `ChannelTransport#read` が呼び出されているのだが、気をつけてほしいのはひとつラッパーが噛んでいるということだ)

### Netty4ServerStreamTransport#read
自身が持っている `ChannelTransport#read` を呼び出すことでQueueからメッセージを取り出しNettyのRequest objectからFinagleのRequest objectへの変換を行っている
QueueはAsyncQueueのため、戻り値はFutureになっている、どちらも `twitter.util`  にあるclassだ

### HttpServerDispatcher#dispatch
自身のsuper classである `GenStreamingSerialServerDispatcher#loop` -> `GenStreamingSerialServerDispatcher#dispatchAndHandleFn` を経由して呼び出されている
`dispatch` 関数ではユーザが用意したサービスにRequestオブジェクトが渡されるようになっている(つまり、read側の終点となっている)

### HttpServerDispatcher#handle
`dispatch` と同じく `GenStreamingSerialServerDispatcher#dispatchAndHandleFn` から呼び出されている
ここからはレスポンスをチャネルに書き込むためのデータの流れになる
渡ってきたResponse型を `Netty4ServerStreamTransport#write` に渡している

### Netty4ServerStreamTransport#write
read同様FinagleのResponseをNettyのResponse objectに変換して `ChannelTransport#write` に書きこんでいる

### ChannelTransport#write
保持しているチャネルに対してメッセージを書き込む
書き込んだあとはNettyの仕組みに従ってユーザへレスポンスが配送される

---

データの流れとしては以上だ、validation処理やclose処理などいろいろハブった部分があったり本当のソースコードはもっと抽象化されていたりと今回書いた流れほど簡潔ではないが
誰かのソースコードリーディングの手助けになればと思います