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
0
6.6k
広告配信システムでのトラフィック計測と実装方法 #ScalaAkiMatsuri
Scala秋祭りで発表した資料です
Kurochan
September 16, 2019
Tweet
Share
More Decks by Kurochan
See All by Kurochan
AWS x さくらのクラウドのハイブリッドクラウドによる安価なフレッツ閉域網接続の実装
kurochan
8
4.1k
GoでTCP Proxyを実装してみよう
kurochan
1
430
サイバーエージェントの広告配信におけるIPoEトラフィックの概況
kurochan
0
210
スケールするというのはどういうことなのか
kurochan
14
4.1k
サイバーエージェントのGitHub Copilot導入と 開発生産性
kurochan
39
37k
Cloudflare Zero Trustを利用したセキュアな開発環境へのアクセス手法の確立
kurochan
10
2.4k
セキュキャンを卒業してその後
kurochan
0
1.1k
サイバーエージェントの実践×実験Snowflake 導入の経緯から最新機能のトライアルまで / How Snowflake Is Used In CyberAgent - Go To the Future
kurochan
0
820
入門Open Policy Agent: Policy as Codeを目指して / introduction-to-open-policy-agent
kurochan
0
470
Other Decks in Technology
See All in Technology
Azure Container Apps + Bicep 〜 こんな感じで運用しています
kaz29
2
450
Google Cloud の AI を支える裏側のインフラを垣間見る!
maroon1st
0
340
SIEMを用いて、セキュリティログ分析の可視化と分析を実現し、PDCAサイクルを回してみた
coconala_engineer
0
280
Tableau事例紹介 / Tableau Case Study of Eureka
kazuya_araki_tokyo
1
190
Meta Quest 3 で動く桜マシマシ WebXR アプリを IBM Cloud Code Engine と Babylon.js で作った話
1ftseabass
PRO
0
120
アクセス制御にまつわる改善 / Improving access control
itkq
0
520
開発生産性大幅アップ!Postman VS Code拡張機能
nagix
2
370
ここが嬉しいABAC ここが辛いよABAC #再解説+補足編
masahirokawahara
1
270
一生覚えておきたい「システム開発=コミュニケーション」〜初めての実務案件振り返りLT〜
maimyyym
0
110
20240418_Google ColabにLLMが搭載されたようなのでPython x データ分析の勉強方法を考えてみる
doradora09
0
120
Terraformあれやこれ/terraform-this-and-that
emiki
8
1.3k
ServiceNow Knowledge 24の歩き方 EYストラテジー・アンド・コンサルティング
manarobot
0
180
Featured
See All Featured
It's Worth the Effort
3n
180
27k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
116
18k
[RailsConf 2023] Rails as a piece of cake
palkan
23
3.9k
VelocityConf: Rendering Performance Case Studies
addyosmani
320
23k
Done Done
chrislema
178
15k
ParisWeb 2013: Learning to Love: Crash Course in Emotional UX Design
dotmariusz
104
6.6k
Debugging Ruby Performance
tmm1
70
11k
What's new in Ruby 2.0
geeforr
337
31k
Designing Experiences People Love
moore
136
23k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
322
20k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
7
1k
Writing Fast Ruby
sferik
621
60k
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/ /
ありがとうございました