広告配信システムでのトラフィック計測と実装方法 #ScalaAkiMatsuri

1f745ff900e1be51aedae18cae76593c?s=47 Kurochan
September 16, 2019

広告配信システムでのトラフィック計測と実装方法 #ScalaAkiMatsuri

Scala秋祭りで発表した資料です

1f745ff900e1be51aedae18cae76593c?s=128

Kurochan

September 16, 2019
Tweet

Transcript

  1. 2.

    黒崎 優太 • Dynalyst 開発責任者 • 業務は Scala + AWS

    • イラスト図解でよくわかる ITインフラの基礎知識 書きました • AWSのAZ(アベイラビリティーゾーン)とは?AZ障 害が起きたときどうすればよいのか 最近ちょっとバズりました @kuro_m @kurochan
  2. 3.

    Scala Matsuri • Wi-Fiを担当しました • かなり好評だったようでよかったです • ネットワークの⼀部にScalaを導⼊ • 発表

    https://speakerdeck.com/kurochan/wi-fi-x-scala-implementing-captive-portal-in-scala-and- deploy-into-number-scalamatsuri
  3. 14.

    RTBのしくみ • これを⾼速に繰り返す 441 %41T "% "% "% ԁ ԁ

    ԁ "% XJO "% औҾ͸NTҎ಺ʹ׬ྃ͠ͳ͚Ε͹ͳΒͳ͍
  4. 16.

    開発している広告システム概況 • ⼊札リクエスト量: 数⼗万リクエスト / 秒 • ⼊札トラフィック: 約8Gbps •

    レスポンスタイム: 100ms以内 • ログの量: 数TB / Day(圧縮状態) ೔ຊͷೖࡳϦΫΤετඵ ೔ຊΞϝϦΧͷϨεϙϯελΠϜ NTFD https://logmi.jp/tech/articles/
  5. 23.

    Akka HTTP • HTTPサーバのフレームワーク • Akka Streamsで実装されている • HTTPで受けたリクエストからパラメータをパースしてファイルに吐き出すの が基本

    • 特に変わった使い⽅はしていません • Dynalystではもともとsprayを使っていたのでAkka HTTPに移⾏しました
  6. 32.

    DynamoDBへのアクセスを多重化する • AWS SDK for Java . が公開され、nettyベースの実装が追加された • nettyを使うとリクエスト部分がノンブロッキングになる

    • Dynalystでは端末情報はDynamoDBに保存しているため、
 リクエストの処理の並列度を⾼めるのは重要 • レスポンスがJavaのCompletableFutureで返ってくるので変換が必要 • scala-java -compatを使ってFutureに変換 • CompletableFutureはfail時にExceptionをCompletionExceptionでラップするので注意
  7. 36.

    パラメータを引き回さないといけない理由 • ⼊札時のログがあるならばトランザクションIDのみURLパラメータで引き回 して、表⽰計測のログと⼊札時のログで集計時にJOINすればよいのでは? • 理論的にはそう • 実際はログの量が多く、あまりやりたくない… • ⼊札のログ:

    1⽇5TBくらい • 広告成果(アプリ起動や課⾦等)について、過去1週間分の⼊札ログと紐付けてレポートを作成 するといったようなユースケースがあるとすぐつらくなる • 集計で必要になるパラメータはURLパラメータを使って表⽰計測サーバまで引き回す
  8. 38.

    困っていたこと • パラメータ数が多い • パラメータを追加するごとに計測URLを⽣成/パースする部分のテストコードに修正が… • 気軽にA/Bテストしたいので追加するハードルを下げたい • URLが⻑い •

    使われなくなったパラメータが残りがち(3ヶ⽉前に⼊札した広告の計測が⾶んできたり, 後⽅互換性…) • 実装上の都合でパラメータ名をわかりやすくしようとするとURLが⻑くなる • 暗号化処理 • 機械学習の予測値等、内部パラメータはひとつひとつ暗号化していた • URLパラメータ改ざんへの耐性 • 不正なリクエストを送って不正に広告収益をあげようとする⼈たちがいる • 改ざん検知のコードのメンテが⾯倒だった
  9. 46.

    暗号化したい • AES / Cipher Block Chaining / PKCS Padding

    • 多くのJVMはIntel CPUのAES-NIが使えるので⼗分な速度で処理が可能 • 前述のtraitにDeflateして暗号化してからBase に変換するメソッドを実装(その逆も) https://ja.wikipedia.org/wiki/ %E % A% %E % F%B %E % %A %E % %A %E % %A %E % %BC%E % %
  10. 47.

    リプレイ攻撃に対する対処 • AES- はInitialization Vectorに128bit必要 • Base にエンコードすると22⽂字くらい必要 • IVはデコード側にも渡さないといけない

    • IVは固定する, 先頭にnonceを⼊れる • URLパラメータでIV分のデータ量を消費せず済む(衝突確率を考慮した上で128bit以下にする) • 前⽅にトランザクションIDが含まれているのでなくても⼤丈夫かもしれない • 例: 現在時刻のミリ秒を16bitで切り捨てて⼊れる
  11. 53.

    ⾃作したかった理由 • Firehoseは「書き込まれる Amazon S オブジェクトに含まれる最も古いレ コードのおおよその到達タイムスタンプ」を⾒てファイルのアップロード先 パスを決定する • /path/to/

    / / / /に配置されるログには2019/09/15 23:59のログが混ざっている可 能性がある • リカバリ作業等で古いログをストリームに投げても
 Firehoseは最新の時刻のパーティションにログを配置する • ログのアップロード時刻ではなく、ログ中のタイムスタンプを⾒て
 パーティショニングがしたい
  12. 59.

    Kinesis Client LibraryをSourceとして使う • Kinesis Client Library = KCL •

    Kinesisを使ったストリーミング処理が簡単に書けるAmazon公式Javaライブラリ • KCLをAkkaStreamのSourceとして使うことができるライブラリがある • AkkaStreamsのおかげでKinesisのキャパシティを
 詰まることなくぴったり使い切れる https://github.com/StreetContxt/kcl-akka-stream
  13. 62.

    • パーティションごとにLocalのストレージにログをバッファリングする • ファイルにバッファリングさせることでメモリの消費量を抑えたかった • パーティションはログの中⾝のタイムスタンプで決定する • ⼀定容量/⼀定時間経過するとS にPutObjectする •

    KinesisRecordを引き回す必要がなければ半分くらいメモリ消費を抑えられ たが… • PutObjectが成功したらアップロードされたオブジェクトのS Pathと関係す るcontextのリストをpushする ログをバッファリングするFlowの⾃作
  14. 64.

    Amazon EMR • Apache Spark on Amazon EMR • クラスタコンピューティングフレームワーク

    • 分散共有メモリモデル • ノードを増やすことでスケールする • Spark MLibという機械学習ライブラリも &.3$MVTUFS .BTUFS 4MBWF 4MBWF 4MBWF 4MBWF
  15. 66.

    いいところ • スケールする • サーバを増やせば… (多い時でCPU Core / Memory TBくらいのクラスタが稼働しています)

    • テストコードが書ける • テストコードがないと複雑な集計を実装するのは厳しい • 例: 広告主やSSPの国に応じて外部DBからタイムゾーンを取得し、
 通貨単位も変換して集計する • 可⽤性をあまり落とさずにスポットインスタンスを活⽤して
 格安でクラスタ運⽤ができる • Sparkのおかげで⼀部のワーカーが落ちても
 ⾃動でリカバリ & 集計は中断されない https://developers.cyberagent.co.jp/blog/archives/ /