LoginSignup
2
1

More than 3 years have passed since last update.

twitter Finagleのデータの流れ

Posted at

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

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

com.twitter.finagle.Http.serve

名前の通りサーバアプリケーションを立ち上げるメソッドだ
overrideで様々な定義がされているが今回は QuickStart 同様 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専用の機能を使うかのフラグを見た後 NioEventLoopGroupEpollEventLoopGroup が使用される(後者がLinux専用のものだ)
BossGroupのほうはThread数が1で固定されているWorkGroupのほうはデフォルトでは8以上だ(多くない?)
他様々なoptionが設定されたあと、Handlerの登録に入る
順番はコメントにも書かれている通り raw => marshalling => framed => bridge の順で適用される(コメントにはないが実際には pipelineInit も適用される)
あっているか自信はないがひとつずつ見ていこう

pipelineInit

長い…
com.twitter.finagle.netty4.http.ServerPipelineInit より HttpServerCodecinitServer メソッド内の各ハンドラが追加される
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呼び出し側の この部分 だ)

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処理などいろいろハブった部分があったり本当のソースコードはもっと抽象化されていたりと今回書いた流れほど簡潔ではないが
誰かのソースコードリーディングの手助けになればと思います

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1