$30 off During Our Annual Pro Sale. View Details »

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

Kurochan
September 16, 2019

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

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

Kurochan

September 16, 2019
Tweet

More Decks by Kurochan

Other Decks in Technology

Transcript

  1. 広告配信システムでのトラフィック計測と

    実装⽅法 #ScalaAkiMatsuri
    株式会社サイバーエージェント AI事業本部

    黒崎 優太 (@kuro_m )

    View Slide

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

    View Slide

  3. Scala Matsuri
    • Wi-Fiを担当しました
    • かなり好評だったようでよかったです
    • ネットワークの⼀部にScalaを導⼊
    • 発表
    https://speakerdeck.com/kurochan/wi-fi-x-scala-implementing-captive-portal-in-scala-and-
    deploy-into-number-scalamatsuri

    View Slide

  4. 最近事業部名がかわりました
    • アドテク本部 → AI事業本部

    View Slide

  5. インターネット広告

    View Slide

  6. RTBのしくみ
    • RTB = Real Time Bidding
    441
    %41T

    View Slide

  7. RTBのしくみ
    • Webページがロードされ、広告タグが発⽕
    441
    %41T

    View Slide

  8. RTBのしくみ
    • 枠情報やユーザ情報とともにbid requestがくる
    441
    %41T
    CJESFRVFTU

    View Slide

  9. RTBのしくみ
    • ⼊札額と表⽰する広告を決定
    441
    %41T
    "%
    "%
    "%
    CJESFRVFTU
    ԁ
    ԁ
    ԁ

    View Slide

  10. RTBのしくみ
    • Open RTBというプロトコルで⼊札
    441
    %41T
    "%
    "%
    "%
    ԁ
    ԁ
    ԁ

    View Slide

  11. RTBのしくみ
    • ⼊札額が⼀番⾼い事業者が勝利する
    441
    %41T
    "%
    "%
    "%
    ԁ
    ԁ
    ԁ
    XJO

    View Slide

  12. RTBのしくみ
    • SSPに広告クリエイティブが送られる
    441
    %41T
    "%
    "%
    "%
    ԁ
    ԁ
    ԁ
    "%
    XJO

    View Slide

  13. RTBのしくみ
    • SSP経由で落札した事業者の広告が展開される
    441
    %41T
    "%
    "%
    "%
    ԁ
    ԁ
    ԁ
    "%
    XJO
    "%

    View Slide

  14. RTBのしくみ
    • これを⾼速に繰り返す
    441
    %41T
    "%
    "%
    "%
    ԁ
    ԁ
    ԁ
    "%
    XJO
    "%
    औҾ͸NTҎ಺ʹ׬ྃ͠ͳ͚Ε͹ͳΒͳ͍

    View Slide

  15. Dynalystとは
    • Dynamic Retargeting for Games
    スマホ向けリターゲティング広告配信プラットフォーム
    トップセールス @⽇本のスマホゲームの中でも⾼いシェア
    ⽇本、アメリカを含む7カ国に配信中
    ユーザごとに最適化した広告を配信
    IUUQXXXEZOBMZTUJP

    View Slide

  16. 開発している広告システム概況
    • ⼊札リクエスト量: 数⼗万リクエスト / 秒
    • ⼊札トラフィック: 約8Gbps
    • レスポンスタイム: 100ms以内
    • ログの量: 数TB / Day(圧縮状態)
    ೔ຊͷೖࡳϦΫΤετඵ ೔ຊΞϝϦΧͷϨεϙϯελΠϜ NTFD

    https://logmi.jp/tech/articles/

    View Slide

  17. 最近⾒ていて楽しい画⾯
    • オートスケーリング系のメトリクス

    View Slide

  18. Contents
    • 計測サーバの実装
    計測の種類
    Akka HTTPを利⽤した計測サーバの実装
    • 計測パラメータの引き回し
    広告で起こる困ったこと
    パラメータの引き回しを楽にする⼯夫
    • ログの転送‧集計
    Amazon Kinesisを活⽤した⾃作ログ転送システム
    Amazon EMRを活⽤した集計システム

    View Slide

  19. 計測サーバの実装

    View Slide

  20. 計測の種類

    View Slide

  21. 計測の種類
    • 広告の表⽰の計測
    • 広告のクリックの計測/遷移
    • Dynalystはリターゲティング広告配信システムのため外部の計測ツール経由
    でユーザの情報を同期しないといけない
    • 例: ユーザがある広告主のゲームをインストールした、ログインした、課⾦した

    View Slide

  22. Akka HTTPを⽤いた計測サーバの

    実装

    View Slide

  23. Akka HTTP
    • HTTPサーバのフレームワーク
    • Akka Streamsで実装されている
    • HTTPで受けたリクエストからパラメータをパースしてファイルに吐き出すの
    が基本
    • 特に変わった使い⽅はしていません
    • Dynalystではもともとsprayを使っていたのでAkka HTTPに移⾏しました

    View Slide

  24. Akka HTTPの実装例
    https://doc.akka.io/docs/akka-http/current/introduction.html

    View Slide

  25. 外部計測ツールとの連携
    "%
    DMJDL
    ֎෦ܭଌπʔϧ

    View Slide

  26. 外部計測ツールとの連携
    DMJDLαʔό
    "%
    DMJDL
    ֎෦ܭଌπʔϧ

    View Slide

  27. 外部計測ツールとの連携
    DMJDLαʔό
    "%
    DMJDL
    ֎෦ܭଌπʔϧ
    DMJDL௨஌

    View Slide

  28. 外部計測ツールとの連携
    DMJDLαʔό
    "%
    DMJDL
    ֎෦ܭଌπʔϧ
    DMJDL௨஌
    0,

    View Slide

  29. 外部計測ツールとの連携
    DMJDLαʔό
    "%
    DMJDL
    ֎෦ܭଌπʔϧ
    SFEJSFDU
    DMJDL௨஌
    0,

    View Slide

  30. 外部計測ツールとの連携
    DMJDLαʔό
    "%
    ֎෦ܭଌπʔϧʹ௨஌͔ͯ͠ΒϦμΠϨΫτ͢Δ
    DMJDL
    ֎෦ܭଌπʔϧ
    SFEJSFDU
    DMJDL௨஌
    0,

    View Slide

  31. 外部計測ツールとの連携
    • 多くの広告主は外部の計測ツールを使ってインストールや起動を計測している
    • 広告のクリックを計測ツールに連携すると広告主は複数の広告配信事業者を横断して

    成果を⽐較することができるため、Dynalystの広告経由のクリック発⽣時に

    計測ツールに通知する必要がある
    • Akka Http Clientを使って通知を送信する

    View Slide

  32. DynamoDBへのアクセスを多重化する
    • AWS SDK for Java . が公開され、nettyベースの実装が追加された
    • nettyを使うとリクエスト部分がノンブロッキングになる
    • Dynalystでは端末情報はDynamoDBに保存しているため、

    リクエストの処理の並列度を⾼めるのは重要
    • レスポンスがJavaのCompletableFutureで返ってくるので変換が必要
    • scala-java -compatを使ってFutureに変換
    • CompletableFutureはfail時にExceptionをCompletionExceptionでラップするので注意

    View Slide

  33. 計測パラメータの引き回し

    View Slide

  34. 計測パラメータの引き回し

    View Slide

  35. 計測パラメータとは
    • レポート作成、分析、外部システム連携のためのパラメータがたくさんあります
    • トランザクションID
    • 端末ID
    • 広告主ID
    • キャンペーンID
    • 通貨レート
    • ⼊札時の各種予測値(CTR, CVR, CPM等)
    • 全部で40〜50種類程度

    View Slide

  36. パラメータを引き回さないといけない理由
    • ⼊札時のログがあるならばトランザクションIDのみURLパラメータで引き回
    して、表⽰計測のログと⼊札時のログで集計時にJOINすればよいのでは?
    • 理論的にはそう
    • 実際はログの量が多く、あまりやりたくない…
    • ⼊札のログ: 1⽇5TBくらい
    • 広告成果(アプリ起動や課⾦等)について、過去1週間分の⼊札ログと紐付けてレポートを作成
    するといったようなユースケースがあるとすぐつらくなる
    • 集計で必要になるパラメータはURLパラメータを使って表⽰計測サーバまで引き回す

    View Slide

  37. パラメータの引き回しを楽にする

    ⼯夫

    View Slide

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

    View Slide

  39. Protocol Buffers
    • Googleが開発したプログラミング⾔語/プラットフォーム⾮依存のデータシ
    リアライズ/デシリアライズのフォーマットとその実装
    • protoファイルでデータ構造を記述すると各種⾔語向けのクラス定義やシリ
    アライズ/デシリアライズのコードが⾃動⽣成できる
    • 後⽅互換性が保ちやすい設計
    https://developers.google.com/protocol-buffers/

    View Slide

  40. Scala x Protocol Buffers
    • コードを⾃動⽣成したかったのでScalaPBを採⽤

    View Slide

  41. Protocol Buffersのシリアライズ
    • 標準でバイナリに変換することはできる
    • toByteArray(), parseFrom(bytes: Array[Byte])
    • URLパラメータに埋め込みたいのでバイナリはそのまま扱えない

    View Slide

  42. Protocol BuffersをURLパラメータに埋め込む
    • Base を採⽤
    • 他⾔語でも容易に扱えるため
    • バイナリを毎回Base に変換するコードを書くのは避けたい、

    できればあまり意識したくない

    View Slide

  43. Protocol BuffersにScalaPBの拡張を⽤いる
    • ⽣成されるcase class/objectに⾃作traitを継承させる

    View Slide

  44. Protocol BuffersにScalaPBの拡張を⽤いる
    ࣗ࡞USBJU

    View Slide

  45. URLを短くしたい
    • ProtocolBuffersにしただけでも可変⻑変数やURLパラメータ名分短くなる
    等、以前よりURLが短くなるような要素はある
    • ProtocolBuffersのwire formatからすると同じような表現が繰り返されそう
    なので何かしら圧縮はできそうである
    • Deflateを採⽤
    • 動作が軽めで他⾔語にもライブラリが存在しそうな汎⽤的なアルゴリズム
    • 前述のtraitにBase に変換する前にDeflateするメソッドを追加(その逆も)

    View Slide

  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 % %

    View Slide

  47. リプレイ攻撃に対する対処
    • AES- はInitialization Vectorに128bit必要
    • Base にエンコードすると22⽂字くらい必要
    • IVはデコード側にも渡さないといけない
    • IVは固定する, 先頭にnonceを⼊れる
    • URLパラメータでIV分のデータ量を消費せず済む(衝突確率を考慮した上で128bit以下にする)
    • 前⽅にトランザクションIDが含まれているのでなくても⼤丈夫かもしれない
    • 例: 現在時刻のミリ秒を16bitで切り捨てて⼊れる

    View Slide

  48. 値が⼊っていない状態を表現する
    • Protocol Buffers(ver )だとoptional/requiredという概念が存在しない
    • プリミティブ値に値がセットされていない場合はプリミティブのデフォルト
    値で埋められる (例: uint => )
    • google/protobuf/wrappers.proto を使って解決

    View Slide

  49. ログの転送

    View Slide

  50. Amazon Kinesisを活⽤した

    ⾃作ログ転送システム

    View Slide

  51. ログ転送の仕組み
    ܭଌαʔό qVFOUE
    ,JOFTJT ετϦʔϜ
    ࠓճ࡞ͬͨͷ͸͜͜ 4 ετϨʔδ

    View Slide

  52. Amazon Kinesis Data Firehose
    • これでできることとほぼ⼀緒では…
    https://aws.amazon.com/jp/kinesis/data-firehose/

    View Slide

  53. ⾃作したかった理由
    • Firehoseは「書き込まれる Amazon S オブジェクトに含まれる最も古いレ
    コードのおおよその到達タイムスタンプ」を⾒てファイルのアップロード先
    パスを決定する
    • /path/to/ / / / /に配置されるログには2019/09/15 23:59のログが混ざっている可
    能性がある
    • リカバリ作業等で古いログをストリームに投げても

    Firehoseは最新の時刻のパーティションにログを配置する
    • ログのアップロード時刻ではなく、ログ中のタイムスタンプを⾒て

    パーティショニングがしたい

    View Slide

  54. Akka Streams
    • Source: メッセージを吐き出すコンポーネント(始点)
    • Flow: メッセージを受け取ってメッセージを送るコンポーネント
    • Sink: メッセージを受け取るコンポーネント(終点)

    View Slide

  55. Akka Streams
    • それぞれのinとoutの型が⼀致している
    • Source,Sinkの2番⽬, Flowの3番⽬の型パラメータは省略

    View Slide

  56. Akka Streams
    https://doc.akka.io/docs/akka/ . . /scala/stream/stream-quickstart.html

    View Slide

  57. 処理の流れ
    • それぞれのinとoutの型が⼀致している
    • Source,Sinkの2番⽬, Flowの3番⽬の型パラメータは省略

    View Slide

  58. 処理の流れ
    • Kinesisからレコードを取り出す
    • ログをバッファリングする
    • (⼀定のサイズ/時間が経過したら)S にレコードをアップロードする
    • Kinesisのチェックポイントを更新する
    • チェックポイント = ストリームをどこまで読み進めたか

    View Slide

  59. Kinesis Client LibraryをSourceとして使う
    • Kinesis Client Library = KCL
    • Kinesisを使ったストリーミング処理が簡単に書けるAmazon公式Javaライブラリ
    • KCLをAkkaStreamのSourceとして使うことができるライブラリがある
    • AkkaStreamsのおかげでKinesisのキャパシティを

    詰まることなくぴったり使い切れる
    https://github.com/StreetContxt/kcl-akka-stream

    View Slide

  60. kcl-akka-streamの使い⽅
    • 最後にmarkProcessed()を

    呼ぶことでKCLのDynamoDBの
    チェックポイントを更新する
    • 処理前に呼ぶ: at most once
    • 処理後に呼ぶ: at least once

    View Slide

  61. ログをバッファリングするFlowの⾃作

    View Slide

  62. • パーティションごとにLocalのストレージにログをバッファリングする
    • ファイルにバッファリングさせることでメモリの消費量を抑えたかった
    • パーティションはログの中⾝のタイムスタンプで決定する
    • ⼀定容量/⼀定時間経過するとS にPutObjectする
    • KinesisRecordを引き回す必要がなければ半分くらいメモリ消費を抑えられ
    たが…
    • PutObjectが成功したらアップロードされたオブジェクトのS Pathと関係す
    るcontextのリストをpushする
    ログをバッファリングするFlowの⾃作

    View Slide

  63. Amazon EMRを活⽤した

    集計システム

    View Slide

  64. Amazon EMR
    • Apache Spark on Amazon EMR
    • クラスタコンピューティングフレームワーク
    • 分散共有メモリモデル
    • ノードを増やすことでスケールする
    • Spark MLibという機械学習ライブラリも
    &.3$MVTUFS
    .BTUFS
    4MBWF
    4MBWF
    4MBWF
    4MBWF

    View Slide

  65. Apache Sparkで集計を実装する
    • 例: 広告主 x ⽇時の粒度で

    クリック数をカウントする
    42-Ͱॻ͍ͨ৔߹
    4QBSLͰॻ͍ͨ৔߹

    View Slide

  66. いいところ
    • スケールする
    • サーバを増やせば… (多い時でCPU Core / Memory TBくらいのクラスタが稼働しています)
    • テストコードが書ける
    • テストコードがないと複雑な集計を実装するのは厳しい
    • 例: 広告主やSSPの国に応じて外部DBからタイムゾーンを取得し、

    通貨単位も変換して集計する
    • 可⽤性をあまり落とさずにスポットインスタンスを活⽤して

    格安でクラスタ運⽤ができる
    • Sparkのおかげで⼀部のワーカーが落ちても

    ⾃動でリカバリ & 集計は中断されない
    https://developers.cyberagent.co.jp/blog/archives/ /

    View Slide

  67. ありがとうございました

    View Slide