Slide 1

Slide 1 text

finagle のすすめ iwag 2015/08/01 Scala Kansai 2015

Slide 2

Slide 2 text

自己紹介 名前:iwag @iwag_org Scala歴:1年半くらい(Scala 力低 い) Scalaのコンパイル時間気になら ない派 技術:C++11, Ruby, elasticsearch, Java

Slide 3

Slide 3 text

発表について ● finagle で検索システムを置き換えをやったので得られた知 見を話します ●

Slide 4

Slide 4 text

流れ ● finagle 紹介 ● ケーススタディ紹介 ● 感想

Slide 5

Slide 5 text

finagle とは一言で言うと ● twitter が作ってるScala の並列 RPC フレームワーク

Slide 6

Slide 6 text

歴史 ● 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

Slide 7

Slide 7 text

finagle は何でないか? ● Web Application Framework ではない ○ Play Framework とは違う ○ ネットワーク・分散システムのフレームワーク ○ Play にはあるのにな〜が多々ある ● ただの Netty のラッパではない ○ netty.io ■ あらゆるところで使われる非同期ネットワークライブラリ ○ 豊富な謎機能 ■ loadbalancer, serversets, stats,

Slide 8

Slide 8 text

finagle とはなにか ● Future, Service, Filter という中心教義 ● それらを使ったスタック群 ○ finagle-core ○ finagle-httpx ○ finagle-thrift ○ finagle-mysql ○ finagle-redis ○ finagle-memcached ○ finagle-stats ○ finagle-serversets

Slide 9

Slide 9 text

Future ● Future は将来手に入る値を持つオブジェクト〜 ○ Scala 界では常識なので省略 ● finagle で使うのは scala.concurrent.Future ではなく com. twitter.util.Future ● scala.*.Future と同じ機能と考えてよい http://docs.scala- lang.org/ja/overviews/core/futures.html ○ 同じものが2つあるがどうするつもりなのだろうか…

Slide 10

Slide 10 text

Service Req 型をうけてFuture[Rep] を返す関数を持つクラス abstract class Service[Req, Rep] { def apply(req: Req): Future[Rep] // apply() はリクエストが来 た時に呼ばれる }

Slide 11

Slide 11 text

Service (2) ● 適用範囲が広い ○ HTTPサーバ:Service[HttpRequest, HttpResponse] ○ Controller: Service[HttpRequest, HttpRequest] ○ MySQLクライアント:Service[MySQLRequest, MySQLResponse]

Slide 12

Slide 12 text

一番簡単なサーバ 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)) }

Slide 13

Slide 13 text

テスト class HelloTest in Specification { "Hellooooo" should { "reply hello" in { val res = Await.result(service(Request())) res.contentString must_== "Hello" } } }

Slide 14

Slide 14 text

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] }

Slide 15

Slide 15 text

図解 Filter Service ReqI ReqO RepO RepI Req Rep 組み合わせたものはService

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

いつ使うか ● Service に重要じゃない機能を足したい ○ ログ出力したい、リトライしたい 、リクエスト量の制御をしたい… etc ● ⇒ これらはFilterにして Service と分離することでコア機能 に集中できる

Slide 18

Slide 18 text

なにがうれしいか ● 重要じゃない機能をフィルタにして外にだせる… ● Filter&Service は独立してるので ● つけたりはずしたりが容易 ● テストしやすい

Slide 19

Slide 19 text

もっと知りたい方は ● ドキュメント ○ 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

Slide 20

Slide 20 text

事例紹介

Slide 21

Slide 21 text

ケーススタディ ● 検索システムのAPIサーバ置き換えプロジェクト ● 元:C++で書かれた独自分散フレームワーク

Slide 22

Slide 22 text

構成 elasticsearch cluster MySQL cluster zookeeper RPC (zeromq, msgpack) LB http 分散フ レーム ワークの ノード クエリ生成 部 (Scala)

Slide 23

Slide 23 text

元のシステム ● 使ってるミドルウェア ○ RPC:zeromq, msgpack, 一部 thrift ○ 名前解決:zookeeper ○ リクエストから内部クエリに変換部:Scala ○ 検索:elasticsearch ○ データストア:MySQL ● 詳しい話は「ドワンゴ elasticsearch」で検索 ● 代替 RPCフレームワークを探した

Slide 24

Slide 24 text

余談:libchan ● 有力だったの docker/libchan ○ 説明 http://yugui.jp/articles/881 ● go の ネットワーク越しに使えるgoroutine ● spdy, msgpack ● きかなくなっちゃいましたね… ○ 開発止まっている

Slide 25

Slide 25 text

finagle 選択の理由 ● zookeeper, thrift, Scala ○ finagle が想定しているシステムに近い ● 内部クエリ生成部(Scala)を発展させてアプリにする ● 真の理由: 社内がPlayだらけだったので違うのが使いた かった

Slide 26

Slide 26 text

service と filter のつくり ● parse andThen buildQuery andThen render andThen searchES andThen fetchMysql

Slide 27

Slide 27 text

最終的には ● 分散させなくてよくね?となり ○ thrift, zookeeper なし ● モノリシックなアプリになりましたとさ

Slide 28

Slide 28 text

最終的な図 elasticsearch cluster MySQL cluster finagle app LB http finagle app finagle app

Slide 29

Slide 29 text

パフォーマンスアップ ● QPS 20% up ● JVM最高 ● 教訓:C++ だからといって速いわけではない ○ 本システムはレイテンシよりはスループットを重視しており、レイテン シを重視するアプリがだったら別の結果になったと思います

Slide 30

Slide 30 text

まとめ ● finagle よかった ● 分散システム→モノリシックアプリ

Slide 31

Slide 31 text

おまけ:デプロイ方法 ● sbt-assembly で jar にまとめて scp ● activator なんてものはない

Slide 32

Slide 32 text

おまけ:使っているライブラリ ● 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使ったほうがよい

Slide 33

Slide 33 text

感想(よかった点)

Slide 34

Slide 34 text

Scala最高 ● C++比でいいところ ○ IDE がある ■ IntelliJ を使用 ○ コンパイルが速い ○ デバッガがある ○ ライブラリがたくさんある ○ Scala書けるエンジニアはたくさんいる ■ もちろんC++を書けるエンジニアもいます

Slide 35

Slide 35 text

基本ドキュメントはない(英語にも) ● テスト(使い方を知るにはテスト) ● ソース ● finatraもかなり参考になる ● google group

Slide 36

Slide 36 text

ソースがモナモナしてない ● Future, Service, Filter 以外難しい概念なし ● ソースも全然モナモナしてなくて読みやすい

Slide 37

Slide 37 text

細かいフィルタ作るのが楽しい ● テストしやすくてよい ● 作ったもの ○ MaintenanceFilter ○ LoggingFilter ○ RequestIdAppenderFilter ○ ExceptionCatchingFilter

Slide 38

Slide 38 text

finagle全部入り ● 例えばscala は mysql のライブラリでもいろいろあって悩む がfinagle-mysql で選ばなくていい ● finagle-xxx ○ http ○ redis ○ mysql ○ memcached ○ thrift ● なかったら諦める

Slide 39

Slide 39 text

他のライブラリを使いたい ● finagle では com.twitter.util.Future ● 普通のライブラリだと scala.concurrent.Future なんで混ぜ て使うのは難しそう ● twitter/bijection/ を使えばいいらしい(やってません)

Slide 40

Slide 40 text

フィルタ間のデータ受け渡し ● フィルタを細かくしたい ● 機能が複数のフィルタにまたがることがある ● 例:リクエストIDをレスポンス、ヘッダ、ログに含めたい ○ リクエストIDを作るところ、レスポンスを作るところ、ログを出すところ が全部別 ○ 1つのフィルタにまとめたら楽なのだが ○ 入力HttpRequest のヘッダにIDを付加するようにした ■ でも入力を加工するのはどうなのよと思った

Slide 41

Slide 41 text

例:リクエストID val service = … andThen appendId andThen …. andThen logging andThen render andThen search ● HttpRequestのヘッダに X-Request-Id を追加 ● search まではHttpRequest ● logging と render ではヘッダからX-Request-Id フィールドから値を取得して ● いい方法あったら教えて下さい

Slide 42

Slide 42 text

DDD的な視点 ● Service = ドメインサービス ○ ドメインサービス多用には批判が多い ○ aka ドメインモデル貧血症 ● 今回のシステムは”””検索”””しか提供していない ○ ビジネスロジックは利用側に持ってもらっている ● なのでDDDやるぞってところとは合わないかも…

Slide 43

Slide 43 text

finagle-stats ● 各種メトリクスを自動で採集 ● twitter-server をいれていると /admin/metrics.json でメトリ クスが見れる ● よさ ○ 調査がやりやすい ○ 障害の検知 ○ レイテンシが見えるのでSLAの監視に用いる予定

Slide 44

Slide 44 text

twitter-sever ● http://twitter.github.io/twitter-server/ ● 全部入り ○ flag, logging, lifecycle, metrics, ○ いらない機能も入ってる…例えば zipkin ○

Slide 45

Slide 45 text

finatra twitter/finatra ● sinatra ライクな finagle クローン ● finagle にないものがある ○ router ○ mustache template ○ logging ○ twitter-server ● finatra 2.0.0のリリース前で使わなかったけど今だったら 使ってもいいと思う(というか使った方がいい) ● https://twitter.github.io/finatra/assets/FinatraSFScala.pdf

Slide 46

Slide 46 text

感想(よくなかった点)

Slide 47

Slide 47 text

Play ならな〜 ● JSON ● Validation ● 社内事情

Slide 48

Slide 48 text

JSONライブラリ ● play-json ○ 単体で使えるようになってるのでよい

Slide 49

Slide 49 text

Validation and mapping ● Validationとモデルへのマッピングに Play の Forms が使い たい ○ ほんとうに欲しかった… ○ play-jsonみたいに独立して使うことができない ● 結局機能の一部しか使わないだろうということで自作したが よくなかった

Slide 50

Slide 50 text

社内的な事情 ● 社内の便利ライブラリがPlay 前提で辛い

Slide 51

Slide 51 text

高負荷時の動作が不安 ● 稼働しておくとロードアベレージが上がり続けるという問題 が発生 ●

Slide 52

Slide 52 text

メトリクスを観察 ● /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,

Slide 53

Slide 53 text

解決 ● MySQL コネクションプールのコネクション数が少なかった ○ 32 ⇒ 256 にして解決 ○ 解決? ○ 今はさばけているがこの負荷を超えても大丈夫なん?? ■ 機材の関係で詳しく調べられず ■ https://github.com/twitter/finagle/blob/develop/finagle- core/src/main/scala/com/twitter/finagle/pool/WatermarkPool .scala

Slide 54

Slide 54 text

まとめ ● finagle について紹介した ○ 重要なのは Servie と Filter ○ finagle モナモナしてなくてわかりやすい ● 検索システムの書き直しをした ○ Scala は最高(C++ 比で) ○ パフォーマンスも上がったしよかった ● 不安はないわけではない ○ ドキュメント ○ 高負荷時の動作