Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
広告配信システムでのトラフィック計測と実装方法 #ScalaAkiMatsuri
Search
Kurochan
September 16, 2019
Technology
1
7.1k
広告配信システムでのトラフィック計測と実装方法 #ScalaAkiMatsuri
Scala秋祭りで発表した資料です
Kurochan
September 16, 2019
Tweet
Share
More Decks by Kurochan
See All by Kurochan
15年入社者に聞く! これまでのCAのキャリアとこれから
kurochan
1
230
入門 電気通信事業者
kurochan
13
5.6k
AWS x さくらのクラウドのハイブリッドクラウドによる安価なフレッツ閉域網接続の実装
kurochan
9
5.7k
GoでTCP Proxyを実装してみよう
kurochan
1
1.1k
サイバーエージェントの広告配信におけるIPoEトラフィックの概況
kurochan
0
490
スケールするというのはどういうことなのか
kurochan
14
4.9k
サイバーエージェントのGitHub Copilot導入と 開発生産性
kurochan
52
46k
Cloudflare Zero Trustを利用したセキュアな開発環境へのアクセス手法の確立
kurochan
10
3.5k
セキュキャンを卒業してその後
kurochan
0
1.4k
Other Decks in Technology
See All in Technology
Tokyo_reInforce_2025_recap_iam_access_analyzer
hiashisan
0
190
PO初心者が考えた ”POらしさ”
nb_rady
0
210
いつの間にか入れ替わってる!?新しいAWS Security Hubとは?
cmusudakeisuke
0
130
Core Audio tapを使ったリアルタイム音声処理のお話
yuta0306
0
190
生まれ変わった AWS Security Hub (Preview) を紹介 #reInforce_osaka / reInforce New Security Hub
masahirokawahara
0
480
Lazy application authentication with Tailscale
bluehatbrit
0
210
United Airlines Customer Service– Call 1-833-341-3142 Now!
airhelp
0
170
データグループにおけるフロントエンド開発
lycorptech_jp
PRO
1
110
Delta airlines Customer®️ USA Contact Numbers: Complete 2025 Support Guide
deltahelp
0
710
Geminiとv0による高速プロトタイピング
shinya337
1
270
KubeCon + CloudNativeCon Japan 2025 Recap
ren510dev
1
390
自律的なスケーリング手法FASTにおけるVPoEとしてのアカウンタビリティ / dev-productivity-con-2025
yoshikiiida
1
17k
Featured
See All Featured
Rebuilding a faster, lazier Slack
samanthasiow
82
9.1k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
233
17k
Statistics for Hackers
jakevdp
799
220k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
7
740
Documentation Writing (for coders)
carmenintech
72
4.9k
YesSQL, Process and Tooling at Scale
rocio
173
14k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
20
1.3k
Writing Fast Ruby
sferik
628
62k
The Art of Programming - Codeland 2020
erikaheidi
54
13k
Mobile First: as difficult as doing things right
swwweet
223
9.7k
The Straight Up "How To Draw Better" Workshop
denniskardys
234
140k
The Pragmatic Product Professional
lauravandoore
35
6.7k
Transcript
広告配信システムでのトラフィック計測と 実装⽅法 #ScalaAkiMatsuri 株式会社サイバーエージェント AI事業本部 黒崎 優太 (@kuro_m )
黒崎 優太 • Dynalyst 開発責任者 • 業務は Scala + AWS
• イラスト図解でよくわかる ITインフラの基礎知識 書きました • AWSのAZ(アベイラビリティーゾーン)とは?AZ障 害が起きたときどうすればよいのか 最近ちょっとバズりました @kuro_m @kurochan
Scala Matsuri • Wi-Fiを担当しました • かなり好評だったようでよかったです • ネットワークの⼀部にScalaを導⼊ • 発表
https://speakerdeck.com/kurochan/wi-fi-x-scala-implementing-captive-portal-in-scala-and- deploy-into-number-scalamatsuri
最近事業部名がかわりました • アドテク本部 → AI事業本部
インターネット広告
RTBのしくみ • RTB = Real Time Bidding 441 %41T
RTBのしくみ • Webページがロードされ、広告タグが発⽕ 441 %41T
RTBのしくみ • 枠情報やユーザ情報とともにbid requestがくる 441 %41T CJESFRVFTU
RTBのしくみ • ⼊札額と表⽰する広告を決定 441 %41T "% "% "% CJESFRVFTU ԁ
ԁ ԁ
RTBのしくみ • Open RTBというプロトコルで⼊札 441 %41T "% "% "% ԁ
ԁ ԁ
RTBのしくみ • ⼊札額が⼀番⾼い事業者が勝利する 441 %41T "% "% "% ԁ ԁ
ԁ XJO
RTBのしくみ • SSPに広告クリエイティブが送られる 441 %41T "% "% "% ԁ ԁ
ԁ "% XJO
RTBのしくみ • SSP経由で落札した事業者の広告が展開される 441 %41T "% "% "% ԁ ԁ
ԁ "% XJO "%
RTBのしくみ • これを⾼速に繰り返す 441 %41T "% "% "% ԁ ԁ
ԁ "% XJO "% औҾNTҎʹྃ͠ͳ͚ΕͳΒͳ͍
Dynalystとは • Dynamic Retargeting for Games スマホ向けリターゲティング広告配信プラットフォーム トップセールス @⽇本のスマホゲームの中でも⾼いシェア ⽇本、アメリカを含む7カ国に配信中
ユーザごとに最適化した広告を配信 IUUQXXXEZOBMZTUJP
開発している広告システム概況 • ⼊札リクエスト量: 数⼗万リクエスト / 秒 • ⼊札トラフィック: 約8Gbps •
レスポンスタイム: 100ms以内 • ログの量: 数TB / Day(圧縮状態) ຊͷೖࡳϦΫΤετඵ ຊΞϝϦΧͷϨεϙϯελΠϜ NTFD https://logmi.jp/tech/articles/
最近⾒ていて楽しい画⾯ • オートスケーリング系のメトリクス
Contents • 計測サーバの実装 計測の種類 Akka HTTPを利⽤した計測サーバの実装 • 計測パラメータの引き回し 広告で起こる困ったこと パラメータの引き回しを楽にする⼯夫
• ログの転送‧集計 Amazon Kinesisを活⽤した⾃作ログ転送システム Amazon EMRを活⽤した集計システム
計測サーバの実装
計測の種類
計測の種類 • 広告の表⽰の計測 • 広告のクリックの計測/遷移 • Dynalystはリターゲティング広告配信システムのため外部の計測ツール経由 でユーザの情報を同期しないといけない • 例:
ユーザがある広告主のゲームをインストールした、ログインした、課⾦した
Akka HTTPを⽤いた計測サーバの 実装
Akka HTTP • HTTPサーバのフレームワーク • Akka Streamsで実装されている • HTTPで受けたリクエストからパラメータをパースしてファイルに吐き出すの が基本
• 特に変わった使い⽅はしていません • Dynalystではもともとsprayを使っていたのでAkka HTTPに移⾏しました
Akka HTTPの実装例 https://doc.akka.io/docs/akka-http/current/introduction.html
外部計測ツールとの連携 "% DMJDL ֎෦ܭଌπʔϧ
外部計測ツールとの連携 DMJDLαʔό "% DMJDL ֎෦ܭଌπʔϧ
外部計測ツールとの連携 DMJDLαʔό "% DMJDL ֎෦ܭଌπʔϧ DMJDL௨
外部計測ツールとの連携 DMJDLαʔό "% DMJDL ֎෦ܭଌπʔϧ DMJDL௨ 0,
外部計測ツールとの連携 DMJDLαʔό "% DMJDL ֎෦ܭଌπʔϧ SFEJSFDU DMJDL௨ 0,
外部計測ツールとの連携 DMJDLαʔό "% ֎෦ܭଌπʔϧʹ௨͔ͯ͠ΒϦμΠϨΫτ͢Δ DMJDL ֎෦ܭଌπʔϧ SFEJSFDU DMJDL௨ 0,
外部計測ツールとの連携 • 多くの広告主は外部の計測ツールを使ってインストールや起動を計測している • 広告のクリックを計測ツールに連携すると広告主は複数の広告配信事業者を横断して 成果を⽐較することができるため、Dynalystの広告経由のクリック発⽣時に 計測ツールに通知する必要がある • Akka Http
Clientを使って通知を送信する
DynamoDBへのアクセスを多重化する • AWS SDK for Java . が公開され、nettyベースの実装が追加された • nettyを使うとリクエスト部分がノンブロッキングになる
• Dynalystでは端末情報はDynamoDBに保存しているため、 リクエストの処理の並列度を⾼めるのは重要 • レスポンスがJavaのCompletableFutureで返ってくるので変換が必要 • scala-java -compatを使ってFutureに変換 • CompletableFutureはfail時にExceptionをCompletionExceptionでラップするので注意
計測パラメータの引き回し
計測パラメータの引き回し
計測パラメータとは • レポート作成、分析、外部システム連携のためのパラメータがたくさんあります • トランザクションID • 端末ID • 広告主ID •
キャンペーンID • 通貨レート • ⼊札時の各種予測値(CTR, CVR, CPM等) • 全部で40〜50種類程度
パラメータを引き回さないといけない理由 • ⼊札時のログがあるならばトランザクションIDのみURLパラメータで引き回 して、表⽰計測のログと⼊札時のログで集計時にJOINすればよいのでは? • 理論的にはそう • 実際はログの量が多く、あまりやりたくない… • ⼊札のログ:
1⽇5TBくらい • 広告成果(アプリ起動や課⾦等)について、過去1週間分の⼊札ログと紐付けてレポートを作成 するといったようなユースケースがあるとすぐつらくなる • 集計で必要になるパラメータはURLパラメータを使って表⽰計測サーバまで引き回す
パラメータの引き回しを楽にする ⼯夫
困っていたこと • パラメータ数が多い • パラメータを追加するごとに計測URLを⽣成/パースする部分のテストコードに修正が… • 気軽にA/Bテストしたいので追加するハードルを下げたい • URLが⻑い •
使われなくなったパラメータが残りがち(3ヶ⽉前に⼊札した広告の計測が⾶んできたり, 後⽅互換性…) • 実装上の都合でパラメータ名をわかりやすくしようとするとURLが⻑くなる • 暗号化処理 • 機械学習の予測値等、内部パラメータはひとつひとつ暗号化していた • URLパラメータ改ざんへの耐性 • 不正なリクエストを送って不正に広告収益をあげようとする⼈たちがいる • 改ざん検知のコードのメンテが⾯倒だった
Protocol Buffers • Googleが開発したプログラミング⾔語/プラットフォーム⾮依存のデータシ リアライズ/デシリアライズのフォーマットとその実装 • protoファイルでデータ構造を記述すると各種⾔語向けのクラス定義やシリ アライズ/デシリアライズのコードが⾃動⽣成できる • 後⽅互換性が保ちやすい設計
https://developers.google.com/protocol-buffers/
Scala x Protocol Buffers • コードを⾃動⽣成したかったのでScalaPBを採⽤
Protocol Buffersのシリアライズ • 標準でバイナリに変換することはできる • toByteArray(), parseFrom(bytes: Array[Byte]) • URLパラメータに埋め込みたいのでバイナリはそのまま扱えない
Protocol BuffersをURLパラメータに埋め込む • Base を採⽤ • 他⾔語でも容易に扱えるため • バイナリを毎回Base に変換するコードを書くのは避けたい、
できればあまり意識したくない
Protocol BuffersにScalaPBの拡張を⽤いる • ⽣成されるcase class/objectに⾃作traitを継承させる
Protocol BuffersにScalaPBの拡張を⽤いる ࣗ࡞USBJU
URLを短くしたい • ProtocolBuffersにしただけでも可変⻑変数やURLパラメータ名分短くなる 等、以前よりURLが短くなるような要素はある • ProtocolBuffersのwire formatからすると同じような表現が繰り返されそう なので何かしら圧縮はできそうである • Deflateを採⽤
• 動作が軽めで他⾔語にもライブラリが存在しそうな汎⽤的なアルゴリズム • 前述のtraitにBase に変換する前にDeflateするメソッドを追加(その逆も)
暗号化したい • 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 % %
リプレイ攻撃に対する対処 • AES- はInitialization Vectorに128bit必要 • Base にエンコードすると22⽂字くらい必要 • IVはデコード側にも渡さないといけない
• IVは固定する, 先頭にnonceを⼊れる • URLパラメータでIV分のデータ量を消費せず済む(衝突確率を考慮した上で128bit以下にする) • 前⽅にトランザクションIDが含まれているのでなくても⼤丈夫かもしれない • 例: 現在時刻のミリ秒を16bitで切り捨てて⼊れる
値が⼊っていない状態を表現する • Protocol Buffers(ver )だとoptional/requiredという概念が存在しない • プリミティブ値に値がセットされていない場合はプリミティブのデフォルト 値で埋められる (例: uint
=> ) • google/protobuf/wrappers.proto を使って解決
ログの転送
Amazon Kinesisを活⽤した ⾃作ログ転送システム
ログ転送の仕組み ܭଌαʔό qVFOUE ,JOFTJT ετϦʔϜ ࠓճ࡞ͬͨͷ͜͜ 4 ετϨʔδ
Amazon Kinesis Data Firehose • これでできることとほぼ⼀緒では… https://aws.amazon.com/jp/kinesis/data-firehose/
⾃作したかった理由 • Firehoseは「書き込まれる Amazon S オブジェクトに含まれる最も古いレ コードのおおよその到達タイムスタンプ」を⾒てファイルのアップロード先 パスを決定する • /path/to/
/ / / /に配置されるログには2019/09/15 23:59のログが混ざっている可 能性がある • リカバリ作業等で古いログをストリームに投げても Firehoseは最新の時刻のパーティションにログを配置する • ログのアップロード時刻ではなく、ログ中のタイムスタンプを⾒て パーティショニングがしたい
Akka Streams • Source: メッセージを吐き出すコンポーネント(始点) • Flow: メッセージを受け取ってメッセージを送るコンポーネント • Sink:
メッセージを受け取るコンポーネント(終点)
Akka Streams • それぞれのinとoutの型が⼀致している • Source,Sinkの2番⽬, Flowの3番⽬の型パラメータは省略
Akka Streams https://doc.akka.io/docs/akka/ . . /scala/stream/stream-quickstart.html
処理の流れ • それぞれのinとoutの型が⼀致している • Source,Sinkの2番⽬, Flowの3番⽬の型パラメータは省略
処理の流れ • Kinesisからレコードを取り出す • ログをバッファリングする • (⼀定のサイズ/時間が経過したら)S にレコードをアップロードする • Kinesisのチェックポイントを更新する
• チェックポイント = ストリームをどこまで読み進めたか
Kinesis Client LibraryをSourceとして使う • Kinesis Client Library = KCL •
Kinesisを使ったストリーミング処理が簡単に書けるAmazon公式Javaライブラリ • KCLをAkkaStreamのSourceとして使うことができるライブラリがある • AkkaStreamsのおかげでKinesisのキャパシティを 詰まることなくぴったり使い切れる https://github.com/StreetContxt/kcl-akka-stream
kcl-akka-streamの使い⽅ • 最後にmarkProcessed()を 呼ぶことでKCLのDynamoDBの チェックポイントを更新する • 処理前に呼ぶ: at most once
• 処理後に呼ぶ: at least once
ログをバッファリングするFlowの⾃作
• パーティションごとにLocalのストレージにログをバッファリングする • ファイルにバッファリングさせることでメモリの消費量を抑えたかった • パーティションはログの中⾝のタイムスタンプで決定する • ⼀定容量/⼀定時間経過するとS にPutObjectする •
KinesisRecordを引き回す必要がなければ半分くらいメモリ消費を抑えられ たが… • PutObjectが成功したらアップロードされたオブジェクトのS Pathと関係す るcontextのリストをpushする ログをバッファリングするFlowの⾃作
Amazon EMRを活⽤した 集計システム
Amazon EMR • Apache Spark on Amazon EMR • クラスタコンピューティングフレームワーク
• 分散共有メモリモデル • ノードを増やすことでスケールする • Spark MLibという機械学習ライブラリも &.3$MVTUFS .BTUFS 4MBWF 4MBWF 4MBWF 4MBWF
Apache Sparkで集計を実装する • 例: 広告主 x ⽇時の粒度で クリック数をカウントする 42-Ͱॻ͍ͨ߹ 4QBSLͰॻ͍ͨ߹
いいところ • スケールする • サーバを増やせば… (多い時でCPU Core / Memory TBくらいのクラスタが稼働しています)
• テストコードが書ける • テストコードがないと複雑な集計を実装するのは厳しい • 例: 広告主やSSPの国に応じて外部DBからタイムゾーンを取得し、 通貨単位も変換して集計する • 可⽤性をあまり落とさずにスポットインスタンスを活⽤して 格安でクラスタ運⽤ができる • Sparkのおかげで⼀部のワーカーが落ちても ⾃動でリカバリ & 集計は中断されない https://developers.cyberagent.co.jp/blog/archives/ /
ありがとうございました