Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Scala@SmartNews_20150221

13,066 views

Published on

Use case of Scala Finagle in SmartNews, Inc.

Published in: Technology
  • Be the first to comment

Scala@SmartNews_20150221

  1. 1. SCALA @ SMARTNEWS 高パフォーマンス広告システムを3ヶ月で作った話 @taketon_
  2. 2. 自己紹介 • @taketon_ • 脳科学の研究していました • 型安全とビールが好きです
  3. 3. SmartNews • ニュースアプリ • 最近1000万DL達成
  4. 4. 地球くん
  5. 5. 広告はじめました • 2014年12月より広告配信開始 • コンテンツとしての広告 を目指しております
  6. 6. 2014年8月 • 12月のローンチに向けて、チーム召集
  7. 7. 必要だったもの • 速いアドサーバー • 最適化機能を持ったオークションシステム • 代理店からの入稿を助ける管理画面 • 様々な画像サイズに対応するクライアント
  8. 8. 動画広告サーバー • 大体2ヶ月位
  9. 9. Finagle • 動画広告の配信サーバーに使用 • 現在 最高2000qpsに対して avg. 20ms
  10. 10. Finagle • Twitter社製OSS-RPCフレームワーク • Akkaと並ぶScala製Future系ライブラリ • Nettyをラップ • "Server as a function"
  11. 11. 選んだ理由 • サーバーが簡単に作れる • Nettyベースでパフォーマンス言うことなし • Futureによる非同期処理ハンドリング • thriftによるIDLでモデル定義 • twitter-server, Tracerによる監視 • 自分が慣れてた(爆
  12. 12. Negative Points • 更新が遅い • 先日ようやく2.11対応された • (finagle-redisはまだ…) • twitter.util.Futureとscala.concurrent.Futureは挙動が違う • ドキュメント少なめ • NewRelicの値がとれない(代わりにTracerを使用する)
  13. 13. 処理の流れ • 型はドキュメント • IO処理が走る部分をFutureとして返す • 基本的には • Request -> JSON parse -> Data load -> Campaign Filtering -> Response
  14. 14. Server as a function • ServerはReq受け取ってRepを返すfunction ! class Service[-Req, +Rep] extends (Req => Future[Rep])
  15. 15. Ad Service • Nettyが立ち上がり`:port`でListeningServerが待 機 ! class AdService extends Service[Request, Response] { override def apply(request: Request): Future[Response] = { // hogehoge } } val server = Http.serve(":port", new AdService()) Await.result(server) * 上記コードはnettyToFinagleフィルタを入れる必要あり
  16. 16. JSON Parse • Jackson Streaming APIを使用 • AdRequestは.thriftファイルに定義 • case classにしなくてもあまり困らない ! trait JsonParser { def read(request: Request):Option[AdRequest] }
  17. 17. Data Load • IO発生で重い処理が走る可能性があることを
 Futureで示している • CampaignContextはvar ! trait ContextLoader { def load(): Future[CampaignContext] }
  18. 18. Data Load • キャンペーンマスター情報はMySQLから非同期でロード • その他実績値と合わせてConcurrentHashMapに格納 • ConcurrentHashMapのAtomicReferenceをCacheとし てobjectに保持 ! trait ContextLoader { def load(): Future[CampaignContext] }
  19. 19. Campaign Filtering • Contextが保持するcampaignからrequest情報に 従ってフィルタリング ! trait CampaignFilter { def filter( request: AdRequest, ctx: CampaignContext ): Option[AdResponse] }
  20. 20. Future Chain • FutureとFutureを逐次的につなげる • コールバックを登録することに相当 • 前の処理の結果を受け取り、新しいFutureを生成 ! def flatMap[B](f: A => Future[B]): Future[B]
  21. 21. Future Chain ! def apply(request: Request): Future[Response] = { val adReqOpt: Option[AdRequest] = JSONParser.read(request) val fAdResOpt: Future[Option[AdResponse]] = adReqOpt.map { adReq => val fCtx: Future[CampaignContext] = ContextLoader.load() fCtx.map { ctx => CampaignFilter.filter(adReq, ctx) } }.getOrElse { Future.value(None) } !
  22. 22. Future Chain ! fAdResOpt.flatMap { adResOpt => adResOpt.map { adRes => val fSessionKeyOpt = RedisService.getKey() fSessionKeyOpt.flatMap { sessionKeyOpt => sessionKeyOpt.map { sessionKey => Future.value(mkResponse(adRes, sessionKey)) }.getOrElse { Future.value(Response(NO_CONTENT)) } } }.getOrElse { Future.value(Response(NO_CONTENT)) } } }
  23. 23. Filter • filterでビジネスロジックと切り離した形でServer の振る舞いを書く class HttpTimeoutFilter ( val timeOutMs: Int ) extends TimeoutFilter[Request, Response]( timeOutMs.milliseconds, new GlobalRequestTimeoutException(timeOutMs.milliseconds), DefaultTimer.twitter){ } ! Http.serve(":port", new HttpTimeoutFilter(100) andThen AdService) !
  24. 24. Routing val muxer = new HttpMuxer() .withHandler("/ad", new AdService()) ! val server = Http.server(":port", muxer) Await.result(server)
  25. 25. パフォーマンスTips • キャンペーンの数が多くなければ、
 CPU-boundにはならない • IO-boundな処理をFutureで包む
  26. 26. パフォーマンスTips • 基本的なFutureやOptionのインスタンス生成コス トは気にならない • Listで引き回さず、Iteratorで処理を書き、最後に toList • mutable.ArrayBufferで組み立ててtoList • Scalaにおける細かい最適化のプラクティス
  27. 27. まとめ • Future使うと楽 • 重そうなところはmutable • でも、過度にチューニングする必要はない • (́-`).。oO(Finagle、流行るといいな)

×