Slide 1

Slide 1 text

広告配信システムでのトラフィック計測と
 実装⽅法 #ScalaAkiMatsuri 株式会社サイバーエージェント AI事業本部
 黒崎 優太 (@kuro_m )

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

インターネット広告

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

計測サーバの実装

Slide 20

Slide 20 text

計測の種類

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Akka HTTPを⽤いた計測サーバの
 実装

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

外部計測ツールとの連携 • 多くの広告主は外部の計測ツールを使ってインストールや起動を計測している • 広告のクリックを計測ツールに連携すると広告主は複数の広告配信事業者を横断して
 成果を⽐較することができるため、Dynalystの広告経由のクリック発⽣時に
 計測ツールに通知する必要がある • Akka Http Clientを使って通知を送信する

Slide 32

Slide 32 text

DynamoDBへのアクセスを多重化する • AWS SDK for Java . が公開され、nettyベースの実装が追加された • nettyを使うとリクエスト部分がノンブロッキングになる • Dynalystでは端末情報はDynamoDBに保存しているため、
 リクエストの処理の並列度を⾼めるのは重要 • レスポンスがJavaのCompletableFutureで返ってくるので変換が必要 • scala-java -compatを使ってFutureに変換 • CompletableFutureはfail時にExceptionをCompletionExceptionでラップするので注意

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

パラメータの引き回しを楽にする
 ⼯夫

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

暗号化したい • 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 % %

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

ログの転送

Slide 50

Slide 50 text

Amazon Kinesisを活⽤した
 ⾃作ログ転送システム

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

kcl-akka-streamの使い⽅ • 最後にmarkProcessed()を
 呼ぶことでKCLのDynamoDBの チェックポイントを更新する • 処理前に呼ぶ: at most once • 処理後に呼ぶ: at least once

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

Amazon EMRを活⽤した
 集計システム

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Apache Sparkで集計を実装する • 例: 広告主 x ⽇時の粒度で
 クリック数をカウントする 42-Ͱॻ͍ͨ৔߹ 4QBSLͰॻ͍ͨ৔߹

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

ありがとうございました