Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rewrite in Finagle

iwagami
August 01, 2015
1.2k

Rewrite in Finagle

iwagami

August 01, 2015
Tweet

Transcript

  1. 歴史 • 2007年 twitter 開始 ◦ Rails のモノリシックなアプリ • 2009年

    クジラでまくり期 • 2011年 Ruby から Scala または Java に移行している ◦ https://blog.twitter.com/2011/finagle-a-protocol-agnostic-rpc- system ◦ http://readwrite.com/2011/07/06/twitter-java-scala ◦ ちなみにこのときは Scala 2.9.X
  2. finagle は何でないか? • Web Application Framework ではない ◦ Play Framework

    とは違う ◦ ネットワーク・分散システムのフレームワーク ◦ Play にはあるのにな〜が多々ある • ただの Netty のラッパではない ◦ netty.io ▪ あらゆるところで使われる非同期ネットワークライブラリ ◦ 豊富な謎機能 ▪ loadbalancer, serversets, stats,
  3. finagle とはなにか • Future, Service, Filter という中心教義 • それらを使ったスタック群 ◦

    finagle-core ◦ finagle-httpx ◦ finagle-thrift ◦ finagle-mysql ◦ finagle-redis ◦ finagle-memcached ◦ finagle-stats ◦ finagle-serversets
  4. Future • Future は将来手に入る値を持つオブジェクト〜 ◦ Scala 界では常識なので省略 • finagle で使うのは

    scala.concurrent.Future ではなく com. twitter.util.Future • scala.*.Future と同じ機能と考えてよい http://docs.scala- lang.org/ja/overviews/core/futures.html ◦ 同じものが2つあるがどうするつもりなのだろうか…
  5. Service Req 型をうけてFuture[Rep] を返す関数を持つクラス abstract class Service[Req, Rep] { def

    apply(req: Req): Future[Rep] // apply() はリクエストが来 た時に呼ばれる }
  6. 一番簡単なサーバ class Hello extends Service[Request, Response] { override def apply(request:

    Request): Future[Response] = { val resp = Response() resp.contentString = "Hello" Future(resp) } } object Foo extends App { def main = { Await.all( Httpx.serve("0.0.0.0:8080", new Hello)) }
  7. テスト class HelloTest in Specification { "Hellooooo" should { "reply

    hello" in { val res = Await.result(service(Request())) res.contentString must_== "Hello" } } }
  8. Filter abstract class Filter[RepI, RepO, ReqO, ReqI] { def apply(r:ReqI,

    next:Service[ReqO, RepI]): Future[RepO] } abstract class SimpleFilter[Rep, Rep] { def apply(r:Req, next:Service[Req, Rep]): Future[Rep] }
  9. Service と Filter の組み合わせ • andThen で Service と Filter

    とまとめられる val httpStatsFilter = new HttpxStatsFilter[HttpRequest](stats) val statsFilter = new StatsFilter[HttpRequest, HttpResponse](stats) val logging = new QueryLoggingFilter() val retry = new RetryingFilter[HttpRequest, HttpResponse](RetryPolicy.tries(3), new NullTimer) val httpServiceWithFilter: Service[HttpRequest, HttpResponse] = statsFilter andThen httpStatsFilter andThen retry andThen logging andThen mux
  10. もっと知りたい方は • ドキュメント ◦ http://twitter.github.io/finagle/guide/ • はじめに(自分のqiita記事) ◦ http://www.qiita. com/iwag@github/items/7d03292749d78c145112

    • SmartNews での事例と紹介 ◦ http://www.slideshare.net/ShigekazuTakei/jissenscala- 20150221-smartnews • 詳解 twitter のシステム ◦ http://www.slideshare.net/maruyama97/twitter-49709690
  11. 構成 elasticsearch cluster MySQL cluster zookeeper RPC (zeromq, msgpack) LB

    http 分散フ レーム ワークの ノード クエリ生成 部 (Scala)
  12. 元のシステム • 使ってるミドルウェア ◦ RPC:zeromq, msgpack, 一部 thrift ◦ 名前解決:zookeeper

    ◦ リクエストから内部クエリに変換部:Scala ◦ 検索:elasticsearch ◦ データストア:MySQL • 詳しい話は「ドワンゴ elasticsearch」で検索 • 代替 RPCフレームワークを探した
  13. 余談:libchan • 有力だったの docker/libchan ◦ 説明 http://yugui.jp/articles/881 • go の

    ネットワーク越しに使えるgoroutine • spdy, msgpack • きかなくなっちゃいましたね… ◦ 開発止まっている
  14. finagle 選択の理由 • zookeeper, thrift, Scala ◦ finagle が想定しているシステムに近い •

    内部クエリ生成部(Scala)を発展させてアプリにする • 真の理由: 社内がPlayだらけだったので違うのが使いた かった
  15. パフォーマンスアップ • QPS 20% up • JVM最高 • 教訓:C++ だからといって速いわけではない

    ◦ 本システムはレイテンシよりはスループットを重視しており、レイテン シを重視するアプリがだったら別の結果になったと思います
  16. おまけ:使っているライブラリ • finagle 6.24.0 ◦ 2015/08/01時点の最新は 6.25.0 ◦ finagle-httpx ◦

    finagle-mysql ◦ finagle-stats • twitter-server 1.10.0 • elasticsearch 1.4 • play-json 2.3.8 • specs 2 2.4.1 ◦ ちなみにfinagle推奨はscalatestなのでscalatest使ったほうがよい
  17. Scala最高 • C++比でいいところ ◦ IDE がある ▪ IntelliJ を使用 ◦

    コンパイルが速い ◦ デバッガがある ◦ ライブラリがたくさんある ◦ Scala書けるエンジニアはたくさんいる ▪ もちろんC++を書けるエンジニアもいます
  18. 例:リクエストID val service = … andThen appendId andThen …. andThen

    logging andThen render andThen search • HttpRequestのヘッダに X-Request-Id を追加 • search まではHttpRequest • logging と render ではヘッダからX-Request-Id フィールドから値を取得して • いい方法あったら教えて下さい
  19. DDD的な視点 • Service = ドメインサービス ◦ ドメインサービス多用には批判が多い ◦ aka ドメインモデル貧血症

    • 今回のシステムは”””検索”””しか提供していない ◦ ビジネスロジックは利用側に持ってもらっている • なのでDDDやるぞってところとは合わないかも…
  20. finagle-stats • 各種メトリクスを自動で採集 • twitter-server をいれていると /admin/metrics.json でメトリ クスが見れる •

    よさ ◦ 調査がやりやすい ◦ 障害の検知 ◦ レイテンシが見えるのでSLAの監視に用いる予定
  21. finatra twitter/finatra • sinatra ライクな finagle クローン • finagle にないものがある

    ◦ router ◦ mustache template ◦ logging ◦ twitter-server • finatra 2.0.0のリリース前で使わなかったけど今だったら 使ってもいいと思う(というか使った方がいい) • https://twitter.github.io/finatra/assets/FinatraSFScala.pdf
  22. Validation and mapping • Validationとモデルへのマッピングに Play の Forms が使い たい

    ◦ ほんとうに欲しかった… ◦ play-jsonみたいに独立して使うことができない • 結局機能の一部しか使わないだろうということで自作したが よくなかった
  23. メトリクスを観察 • /admin/metrics.json のメトリクスを観察 • MySQLクライアントのpool_waiterが増えるとロードが爆発 していた "clnt/192.168.122.184:3306/failure_accrual/revivals" : 0,

    "clnt/192.168.122.184:3306/load" : 8.0, "clnt/192.168.122.184:3306/loadbalancer/adds" : 1, "clnt/192.168.122.184:3306/loadbalancer/available" : 1.0, "clnt/192.168.122.184:3306/loadbalancer/load" : 91.0, "clnt/192.168.122.184:3306/loadbalancer/removes" : 0, "clnt/192.168.122.184:3306/loadbalancer/size" : 1.0, "clnt/192.168.122.184:3306/pending" : 7.0, "clnt/192.168.122.184:3306/pool_num_too_many_waiters" : 0, "clnt/192.168.122.184:3306/pool_num_waited" : 6410914, "clnt/192.168.122.184:3306/pool_size" : 64.0, "clnt/192.168.122.184:3306/pool_waiters" : 83.0, "clnt/192.168.122.184:3306/received_bytes" : 16468576884,
  24. 解決 • MySQL コネクションプールのコネクション数が少なかった ◦ 32 ⇒ 256 にして解決 ◦

    解決? ◦ 今はさばけているがこの負荷を超えても大丈夫なん?? ▪ 機材の関係で詳しく調べられず ▪ https://github.com/twitter/finagle/blob/develop/finagle- core/src/main/scala/com/twitter/finagle/pool/WatermarkPool .scala
  25. まとめ • finagle について紹介した ◦ 重要なのは Servie と Filter ◦

    finagle モナモナしてなくてわかりやすい • 検索システムの書き直しをした ◦ Scala は最高(C++ 比で) ◦ パフォーマンスも上がったしよかった • 不安はないわけではない ◦ ドキュメント ◦ 高負荷時の動作