SlideShare a Scribd company logo
1 of 35
Scala、DDD、Akkaで立ち向かう
広告配信システムに課せられた100msの制約
株式会社マイクロアド
松宮 康二 (まっつー)
【マイクロアド】〜ネット広告を支える技術を知る〜
#microad_CAMPHOR
2
● 撮影OKです
● スライドは公開します
● 色々な話を詰め込んでるので
興味ある所だけ聞いてもらえたら幸いです
● 時間足りなくて端折るスライドあると思いますの
で、懇親会で話しましょう!
自己紹介
● 名前: 松宮 康二(まっつー)
● 最近触ってるもの
● 最近の趣味
○ ラーメン作り
● Twitter
○ @mattsu6666
3
もくじ
● 広告配信システムの宿命 (5分)
● なぜScala, DDD, Akkaなのか (14分)
● 性能面の具体的な工夫 (10分)
● 可読性を維持する方法 ←スライド作ってから35分の発表では収まらない事に気付いた
● まとめ (1分)
4
割とボリュームが多くなってしまったので
駆け足気味で進みますm(__)m
5
広告配信システムの宿命
100msの制約
● SSPがDSPへ入札要求をしてから100ms以内に返却することが必須条件
● 100msを超えた場合はタイムアウトで入札が拒否される
● レイテンシを含む時間なので現実的には遅くても30ms程度で処理する
● 入札できない = 落札されない = 売上を生まない = 倒産
6
100msの制約を守ることは業務が成立する必要条件
SSP
DSP A
DSP B
DSP C
WEBページ
広告枠
入札要求
100ms以内に応答しないとタイムアウト
応札(50ms)
広告をリクエスト
広告をレスポンス
応札(100ms)
応札(150ms)
入札要求
入札要求
50msで応答してい
るので入札成功
100msで応答して
いるので入札成功
150msで応答して
いるので入札失敗
1つのエンドポイントに全ての機能が詰まっている
● 一般的なWebアプリ等と異なり、エンドポイントはSSP毎に1つだけ
○ 設計次第だがマイクロアドではSSP毎にエンドポイントを分けている
○ 1つのAPIに対して機能追加し続けるイメージ
● ビジネスのスケールとともに機能は増え続ける
● 機能が増えても100msの制約は不変
● しかし機能追加は避けては通れない
7
機能追加は業務を継続する必要条件
SSP A
DSP
入札要求
POST /rtb/a
SSP B
入札要求
POST /rtb/b
SSP C
入札要求
POST /rtb/c
位置情報を使う機能
ユーザの性別とか判定する機能
ユーザと似たユーザを検索する機能
行動履歴を使う機能
ブラックリストユーザ判定機能
ヽ(・ω・ヽ*) PDM
オフライン購買データを使う機能
新機能追加して!
RTBの内部処理
・・・
機能追加をすればする程、RTBの内部処理は
複雑かつ高負荷になっていく。
増え続ける広告在庫
● ビジネスのスケールとともに扱う広告在庫は増大 ※広告在庫とは広告の表示可能回数のこと
● 1度のRTBで出来るだけ多くの広告を配信対象にしたい
● 多くの広告在庫を扱える = より精度の高い広告を配信可能 = 高い広告効果
○ また、たくさん表示可能なため単純に売上が伸びる
● 一方で扱う広告の増大は処理負荷の増大に直結
● 少なくても業務は成り立つけど、スケールしない
8
多くの広告在庫を扱えることは業務が成立する十分条件
SSP DSP
入札要求
応札
化粧品関連のページから
リクエスト
うーん・・・とりあえず健
康関連? DSP 化粧品が最適!(略)
入札要求
応札
DSP(略)
入札要求
応札
(タイムアウト)
化粧品
関連
健康
関連
旅行
関連
広告在庫
健康
関連
旅行
関連
広告在庫
旅行
関連
健康
関連
化粧品
関連
広告在庫
・・・
お、おおすぎる!
処理が間に合わねぇ
広告在庫が少ないと・・・ 広告在庫が多いと 広告在庫が多過ぎると
処理が間に合わず機会損失が発生するのは
もったいない!
こうならないようにエンジニアが頑張る
増え続けるリクエスト
● ビジネスのスケールとともに受けるリクエストは増大
● 出来る限り多くのリクエストを受けたい
● 大量のリクエスト = 入札機会の増大 = 売上アップ
● 大量のリクエストを捌きたいなら・・・
○ サーバを増やす
○ サーバのスペックを上げる
● しかし・・
○ サーバは高い(1台辺り10~20万)
○ ラックに限りがある
○ 1台辺りの収益率が落ちるのは本末転倒
● よって、富豪的アプローチで万事解決とは行かない
● ハードに頼り切るのではなく、ソフトウェアも限界までチューニング
9
大量のリクエストが捌けることは業務が成立する十分条件
日毎のリクエスト数 ※軸の値は伏せてます
増加傾向にある
まとめると
● 100msの制約を守ることは業務が成立する必要条件
● 機能追加は業務を継続する必要条件
● 多くの広告在庫を扱えることは業務が成立する十分条件
● 大量のリクエストが捌けることは業務が成立する十分条件
10
このようなアドテク特有の課題をどうやってクリアするか?
100msの制約
機能追加
多くの広告在庫を扱える 大量のリクエストが捌ける
業務
業務課題に対するアプローチとキーワード
● プログラミング言語にScalaを採用
○ 関数型プログラミング
○ オブジェクト指向プログラミング
○ JVM言語
● ソフトウェアの設計手法にドメイン駆動設計(DDD)を採用
○ 3層 + ドメインアーキテクチャ
○ ドメインモデル
○ ユビキタス言語
● 主要なフレームワーク・ツールキットにAkkaを採用
○ 並行並列処理
○ リアクティブシステム
○ アクターモデル
11
次のスライドから詳細な説明をしていきます
12
なぜScala, DDD, Akkaなのか
Scala
● JVM言語の1つ
● 静的型付け言語
○ 型推論・パターンマッチング
● 小規模なスクリプトから大規模システムの構築まで幅広く利用可能
○ Scalaはscalable languageに由来する
● すべてのJava製ライブラリと相互運用可能
● オブジェクト指向と関数型プログラミングの概念を持つ
● 充実したコレクションAPI
○ ループ処理ではなく述語関数によって表現
13
object Main extends App {
println("Hello world")
}
Seq(1,2,3,4,5)
.map(_ * 2)
.filter(_ > 5)
> Hello world
> Seq[Int] = List(6, 8, 10)
Hello worldの例 述語関数を使った例(for文を使わずにループ処理)
なぜScalaなのか
● 関数型言語の特徴を持っており、シンプルに記述できる
○ イミュータブル
○ 副作用を発生させづらい構文(例えばif文ではなくif式)
○ nullを発生させないデータ構造
● オブジェクト指向言語の特徴を持っており、DDDとの相性が良い
○ ポリモーフィズム(最も重要!) ※後述
○ カプセル化
○ 継承
● JVM言語の安心感
○ 十分な実績を持つJavaとの互換性
○ JVM上で動作するため、Javaに近い性能を期待できる
○ コンパイル言語なので、型安全による安心感
14
関数型言語はなぜシンプルなのか
● 関数型プログラミングは純粋関数だけを使う
○ 純粋関数とは副作用がない関数
● 副作用とは関数内で副次的に発生するアクション
○ グローバル変数に再代入する
○ データベースを参照・更新する
○ I/O
○ 例外を投げる等
● 純粋関数は入力に対して必ず同じ出力がある
● システムの状態に依存しないので...
○ 頭の中で状態を追いながら読まなく良い
○ テストコードが書きやすい
15
class Auth {
public void auth(Token token) {
token.set(getAuth())
}
}
副作用があるケース(コードはJava)
class Auth {
def auth(token: Token): Token = {
token.copy(getAuth())
}
}
副作用がないケース
返り値(結果型)がvoidかTokenの違いがある
副作用があるケースは「 tokenを更新」
副作用がないケースは「 tokenを新規作成」
ポリモーフィズムが重要な理由
● ポリモーフィズム(多態性)はインタフェースに実装できる性質
○ オブジェクト指向以前もポリモーフィズムは可能だったが、言語レベルで安全に実装できるようになった (だからこそ重要)
● インタフェースによってプログラムをより抽象的・汎用的に記述できる
○ 例えばUNIXのIOデバイスドライバはopen, close, read, write, seekの標準機能を提供する
○ もしインタフェースが統一されていなかったら、デバイス依存のコードになり用途が限られる
● インタフェース同士が依存し合うことで高いモジュール性・疎結合性を得る
● ポリモーフィズムによって、ソースコードの依存関係を絶対的に制御(※後述)
16
モジュール同士が抽象に依存
具象クラスを自由に付け替えられる => 疎結合
UNIXはデータの入出力をファイルとして扱う => デバイス非依存
DDD
● DDD(ドメイン駆動設計)はソフトウェア設計手法のこと
○ 技術では無く、ドメイン知識(業務知識)を中心に考える
● 事業価値を最大化することが我々の究極の目標
● しかし実際には...
○ ドメインエキスパート(PDMなどドメインに詳しい人)は事業価値の向上に注力
○ エンジニアは業務上の問題を技術的に解決しようとする
○ エンジニアによって翻訳されたソフトウェアになってしまう
○ 両者のコミュニケーションの相違が起こってしまう
● DDDはドメインエキスパートとエンジニアが共通認識(ユビキタス言語)を
持ったソフトウェアを作る(コードがドメインモデルを反映)
17
(・ω・*) PDM
「広告」には「画像」とか「動画」とかの種類があるんだよ
なぁ。
「広告主」がどっちか選んで「 登録」するんだよねぇ
エンジニア (,,゚Д゚)
画像とか動画とかいってもフォーマット色々あるし、「 JPEG
広告」「PNG広告」「MP4広告」みたいな感じで実装すっか。
生成ロジックを統一したいし、「 ユーザ」が「広告ジェネレー
タ」を使って「生成」するようにしよっと。
下の会話では両者が違う言葉を使っている。
この状況が続くと、両者の認識のズレは拡大し、いつかプロ
グラムの修正が困難な状況に陥る可能性が高い
ドメインモデルをピュアにすべきな理由
● ドメインモデルは何にも依存させるべきではない(ピュアにすべき)
● さらに、ビジネスルールは技術的関心事に左右されるべきではない
● ビジネスルールとはドメインモデルの構成要素
○ "Business rules describe the operations, definitions and constraints that apply to an organization. Business rules can apply to people,
processes, corporate behavior and computing systems in an organization, and are put in place to help the organization achieve its goals."
○ つまり、ビジネス活動の中で意思決定を行うための規則
○ 個人的には、コンピュータが消滅しても失われないルールのことだと思ってる
○ 僕らが一番守りたいのはビジネスルール
18
※広告の例だと
分かりづらかっ
たので
お店を例にしま
した
ECサイト ~インターネット時代~商品を買う例
お店 ~近代~ お店 ~昔~
● 合計の求め方
● 小計の求め方
● 消費税の求め方
などは「会計システム」「POSシステ
ム」「そろばん」(技術的関心事)に左
右されるべきじゃない!
引用: https://en.wikipedia.org/wiki/Business_rule
巨大なモノリシックなアプリのメンテナビリティ
● 広告配信システムは非機能要件的にどうしてもモノリシックな一枚岩に
● 一枚岩故にドメインはめちゃくちゃ複雑
● 一枚岩なのに色んなシステムと接続しまくる
● 何も考えずに作ったらメンテナビリティは容易に悪化
● DDDを取り入れ多層アーキテクチャによってドメインモデルを保護
● レイヤーの依存関係を厳格にし、影響範囲の局所化と明確化
19
プレゼンテーション層
データソース層
アプリケーション層 ドメイン層
A
B
AはBに依存している
HTTPやRPCなどの入力の
インタフェースを担当
ユースケースを担当
(ビジネスロジック)
DB接続周り担当
ビジネスルール担当
(ドメインモデル)
Akka
● Scala製の並行並列処理に特化したツールキット(フレームワークとライブラリを合わせたやつ)
● 主にLightbend社によって開発・メンテされているOSS
● 並行並列、分散システムを簡単に記述するためにアクターモデルを提供
○ アクターモデル自体は1973年に登場
○ クラウドコンピューティングの進歩とムーアの法則の終焉などでスケールアウトが主流になってきたことで
再び注目を浴びてきた
● アクター以外にAkka Streams, Akka Clustring, Akka HTTP等様々なコンポーネントを提供している
● Akkaはリアクティブ宣言に基づいて設計されている
○ 「リソースを効率よく利用し、アプリケーションをを自動的にスケールできるようにすること」 (Akka実践バイブルより引用)
20
引用: https://www.reactivemanifesto.org/ja
アクターモデル
● アクターはメッセージ駆動の計算実体(スレッドに似ている)
○ アクターは非同期に実行される
● アクター同士(数千・数百万)が協調して1つのアプリケーションを構成する
● アトミックやロックを使わず、安全に並行並列処理が実装できる
○ アクターはメールボックス(メッセージキューみたいな)を持っていて、1度に1つ処理
● アクターはfire-and-forget(撃ちっぱなし)の性質によって、非同期処理を実現している
● スケーラビリティのためにアクター同士は疎結合になっている
○ 空間/位置: 位置透過性とも。同じノード、異なるノード等、どこにアクターがいても良い
○ 時間: アクターはタスクの完了について何も保証しないし、期待もしない
○ インターフェース: アクター同士は何も共有しない。インタフェースが正しいかとか関係ない
21
Actor
Actor
Actor
メッセージ送信
メッセージ送信
メッセージ送信
アクターシステム
メールボックス
(メッセージキュー)
アクターは非同期で動作するのでア
クターの数だけ並行並列に動作可
能
ロックとかアトミックな操作は一切不
要
メッセージを受信し
て何か処理
スループットではなくレイテンシを高める
● アクターモデルによって、並行並列処理が安全に実装可能
● スレッドでは実現出来なかったスケールアウトも可能
● アクターによって処理を細かい単位で分離するので、局所的にスケール可能
● よって、Akkaを採用することにした
● しかし・・・良いことばかりではない
○ 高い学習コスト
○ アプリケーションの性質上、コアなロジックはアクターによるスケールアウトが難しい(通信コストが無視出来ない)
○ まだ発展途上のOSSのため、バージョン上がるとガッツリAPIが変わってる
● スループットは変わらないが、レイテンシは高めたい(アクターモデルの恩恵を享受)
○ 全体をそれなりに早くではなく、1リクエスト辺りの応答速度を高めたい
○ 大量のリクエストを満遍なくタイムアウトさせるより、少量のリクエストを確実に返せた方が良い
○ そのためにはアクターモデルを活用した並行並列処理が使いやすい
22
リクエスト1の処理
リクエスト2の処理
リクエスト1の処
理
リクエスト1の処
理
リクエスト2の処
理
リクエスト2の処
理
スレッド1
スレッド2
200ms
スレッド1
スレッド2
200ms
200msでどちらも2件のリクエストを
捌いた。
しかし、左は200msかかってしまっ
てタイムアウトになった
一方で右は100msで応答したので
入札できた
広告配信システムでは100msの制
約があるのでタイムアウト!
超理想の話なので実際にはこんな
綺麗に並列化は難しい
なぜScala, DDD, Akkaなのか
● Scala
○ オブジェクト指向、関数型のメリットを享受できる
○ JVM上で動作するので性能面での安心感
○ 強力な型システムによって規模の拡大が原因でメンテナビリティが低下しづらい等
● DDD
○ 複雑なドメインに立ち向かう手段
○ ビジネスルールに集中でき、事業価値を高めやすい
○ Scalaと相性が良い(オブジェクト指向との相性が良い)
● Akka
○ 並行並列処理、分散処理を安全に実装できる
○ 様々な便利なコンポーネントが使える
23
24
性能面の具体的な工夫
オンメモリ戦略
● アプリケーションのボトルネックの大部分はI/O
○ 通信コスト
○ データを検索するコスト
● 載せれるデータはローカルのメモリ上に持てるだけ持つ
○ いわゆるオンメモリ戦略
● オンメモリ化の条件
○ ある程度の更新間隔を許容(数分~数十分は古くても良い)
○ 件数が膨大過ぎない(メモリに載る容量か)
○ 揮発しても良いデータ(どこかで永続化されてる必要がある)
● 以上の条件を満たせばメモリに載せる
● 単純な戦略だが最も効果的
● ただし、メモリには限界があるので無敵ではない
25
アプリ DB
キャッシュ
定期的にDBのデータをキャッシュ
アプリ DB
毎回DBに問い合わせるので遅い
常に最新のデータを取得できるが、
本当にその必要がある?
速度を犠牲にしてまで最新データにこだわるべき
か?
最新のデータは使えないが、ローカルのメモリに
キャッシュするので高速。
ただし、データの容量等の様々な制限はある
引用: https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html
メインメモリの参照は100ns
SSDのランダムリードは16μs ≒ 16000ns
単純計算で160倍の速度差
さらに、ネットワークのレイテンシを考えればオン
メモリ化が如何に効果的かわかる
整合性の担保
● オンメモリ戦略を使った場合の考慮点として整合性がある
● いくら古いデータを許容できるといっても、過去の異なる時点のデータを参照するのは不味い
● そこで、IOモナドを利用してデータの取得と更新タイミングを分離(更新だけ遅延させる)
○ IOモナドって何・・? => 関数合成出来て、遅延評価が可能な性質が重要。厳密な定義は知りません。
● DBへの問い合わせを先にやり、全てのデータが揃った時点で、一括でオンメモリのデータを差し替える
● これによってほぼ整合性を担保できる(厳密にはDBの問い合わせタイミングが若干ズレるがそこは許容)
● 万一DBの問い合わせが一部失敗した場合は、全てロールバックする
● ただし、一時的に使用するメモリが本来保持するデータ量の2倍になる
26
アプリ
DB A
新キャッシュ
DB B
DB C
キャッシュ
アプリ
新キャッシュ
キャッシュ各種DBからデータを取得していく
が、全てのデータが揃うまで、既
存のキャッシュは更新しない
全てのデータが揃ったタイミングで既存のキャッ
シュを新キャッシュで置き換える
(参照が切り替わるだけ)
またキャッシュ参照時にはリエントラントロックを
かける
リアルタイムなデータはインメモリデータベース
● オンメモリ戦略を適用できないデータはどうするか?
○ リアルタイム性が求められるデータ
○ ローカルのメモリには乗り切らないデータ
● リモートホストのインメモリデータベースにキャッシュする
○ 通信は発生するがデータは高速で読める
○ マイクロアドでは主にRedisを利用 (NoSQLの一つ、KVSとも)
● Redisを活用することで、リアルタイムにデータを高速に読み込めるがKVSの特徴を理解した設計が必要
● 効率良くデータを取得するために、アプリケーションに特化したスキーマで構成する
● 例えばユーザに行動履歴が紐づく場合は単なるString型(Key:Value形式)よりもHash型の方が効率が良い
○ レコードに複数のデータを含む場合は、MessagePack等で事前に圧縮しておく
27
user1_action1
user1_action2
user1_action3
user2_action1
user2_action2
http://aaa.com
http://bbb.com
http://ccc.com
http://aaa.com
http://bbb.com
key value
user1
user2
http://aaa.com
http://bbb.com
http://ccc.com
http://aaa.com
http://bbb.com
key value
action1
action2
action3
field
action1
action2
String型 Hash型
キーを単純にしたことで検索性能が改善
本質でない処理を切り離す
● 広告配信システムの本領は入札要求に対して入札すること
○ しかし、それ以外にも色々やっている(ログの書き出し, DBの更新, 他サービスとの連携等)
● 例えば、入札は出来る限り高速に応答したいが、ログの書き出しは多少遅くても良い
● 本質でない処理にCPUのリソースを割くのは勿体ない。そこでアクターモデルを活用
○ fire-and-forgetの性質によって応答を待つ必要はない
○ アクターは位置透過性なのでログの書き出しはリモートホストで処理してもいい
● ただし、メッセージが到達する信頼性は「at-most-once」
○ つまり、メッセージは喪失する可能性があるので重要なメッセージは送れない
○ at-least-onceを保証する方法も一応可能。ただアクターモデルの旨味が消える
28
RTB処理はログ記録の応答を一切待たないの
でログ記録の影響を受けない
ログ記録がリモートホストに存在する場合、ログ
は喪失する可能性がある
ログの重要度によって、対応方法を変える必要
がある
←ログが消滅!!!
重要なログだったらヤバイ・・・
29
可読性を維持する方法
統一したコードフォーマット
● チーム開発ならコードフォーマットの統一は絶対した方がいい
● みんながバラバラのフォーマッタを使っていると、無駄なコード修正が入って本質を見落とす可能性
● ScalaならScalafmtが良さげ
○ 他にもscalariformとかIntellijの設定を共有する方法もある
○ まぁお好みでって感じだが、体感Scalafmtが一番使われてそう
30
object ScalafmtTest extends {
val aaa = "MicroAd"
def bbb(): Int = {
val a = 1
val b = 2
a + b
}
}
Scalafmt前
object ScalafmtTest extends {
val aaa = "MicroAd"
def bbb(): Int = {
val a = 1
val b = 2
a + b
}
}
Scalafmt 後
レイヤー構造に基づいたクラス設計
● レイヤーの構成方法は三者三様
○ レイヤードアーキテクチャ, クリーンアーキテクチャ, オニオンアーキテクチャ, ヘキサゴナルアーキテクチャ, 3層 + ドメインモ
デル等
○ 色々あるし、独自にカスタマイズしても問題無い
● 一番重要なのはドメインモデルをピュアにすること
● そして、レイヤーの依存関係をないがしろにしないこと
○ ScalaならsbtのdependsOnを利用するとコンパイラレベルで強制可能
● レイヤー構成を定めると修正箇所を局所化できる
● また、設計の指針になるため、人によって実装方法がブレ辛い
31
プレゼンテーション層
データソース層
アプリケーション層 ドメイン層
HTTPやRPCなどの入力の
インタフェースを担当
ユースケースを担当
(ビジネスロジック)
DB接続周り担当
ビジネスルール担当
(ドメインモデル)
ドメイン層は誰にも依存していない。
つまり、ドメイン層以外が修正されてもドメイン
層には何も影響がない。
何も影響がない状況を保ち続けるのが超重
要!!!
インタフェースに依存させる
● ポリモーフィズムを活用してクラス同士の抽象度を高く保つ
● 抽象的であればある程、変更に柔軟なコードになる
● 「依存関係逆転の原則」を利用すると依存関係は思いのまま
32
インフラ層 ドメイン層
Repository
Repository
Impl
Domain
Service
Repository
Impl ドメイン層からインフラ層は
参照してはいけない
インタフェースをドメイン層に置けば
依存関係を保ったまま参照できる
まとめ
● 広告配信システムの成立条件のお話をした
○ 100msの制約を守ることは業務が成立する必要条件
○ 機能追加は業務を継続する必要条件
○ 多くの広告在庫を扱えることは業務が成立する十分条件
○ 大量のリクエストが捌けることは業務が成立する十分条件
● 以上の成立条件を満たすためにScala, DDD, Akkaによるアプローチを紹介した
○ Scalaは「関数型言語」、「オブジェクト指向言語」、「JVM言語」の視点から
○ DDDは「複雑なドメインに立ち向かう手段」として
○ Akkaは「並行並列処理を安全に実装」するため
● そして「性能面の具体的な工夫」として、マイクロアドで実践している内容を挙げた
○ オンメモリ戦略
○ 整合性の担保
○ リアルタイムなデータはインメモリデータベース
○ 本質でない処理を切り離す
● 加速性を維持する方法ではDDDを実践していくコツを紹介した ※今回はしてない
33
We Are Hiring!!
34
マイクロアドでは、広告配信システムを一緒に作りたい人を
募集しています!
https://recruit.microad.co.jp/
公式アカウント @microad_dev もよろしくお願いします。
参考文献
● Scalaスケーラブルプログラミング第3版
● Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関
数型徹底ガイド
● Clean Architecture 達人に学ぶソフトウェアの構造と設計
● 実践ドメイン駆動設計
● Akka実践バイブル アクターモデルによる並行・分散システムの実現
● 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向
の実践技法
35

More Related Content

What's hot

ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところY Watanabe
 
オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ増田 亨
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)
ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)
ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)Tokoroten Nakayama
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門泰 増田
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Akihiro Suda
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?Yoshitaka Kawashima
 
「速」を落とさないコードレビュー
「速」を落とさないコードレビュー「速」を落とさないコードレビュー
「速」を落とさないコードレビューTakafumi ONAKA
 
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~Miki Shimogai
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころJunya Hayashi
 
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜Takahiro Inoue
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織Takafumi ONAKA
 
世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計増田 亨
 
正しいものを正しくつくる
正しいものを正しくつくる正しいものを正しくつくる
正しいものを正しくつくるtoshihiro ichitani
 
ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説増田 亨
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!mosa siru
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームApache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームKouhei Sutou
 
DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)
DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)
DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)MicroAd, Inc.(Engineer)
 

What's hot (20)

ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
 
オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)
ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)
ラボラトリーオートメーションのためのソフトウェア思想教育(非プログラマ―が知っておくべきプログラミングの本質)
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
「速」を落とさないコードレビュー
「速」を落とさないコードレビュー「速」を落とさないコードレビュー
「速」を落とさないコードレビュー
 
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころ
 
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計
 
正しいものを正しくつくる
正しいものを正しくつくる正しいものを正しくつくる
正しいものを正しくつくる
 
Spring Cloud Data Flow の紹介 #streamctjp
Spring Cloud Data Flow の紹介  #streamctjpSpring Cloud Data Flow の紹介  #streamctjp
Spring Cloud Data Flow の紹介 #streamctjp
 
ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームApache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォーム
 
DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)
DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)
DDD&Scalaで作られたプロダクトはその後どうなったか?(Current state of products made with DDD & Scala)
 
MLOps入門
MLOps入門MLOps入門
MLOps入門
 

More from MicroAd, Inc.(Engineer)

20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用
20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用
20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用MicroAd, Inc.(Engineer)
 
Kafka Connect:Iceberg Sink Connectorを使ってみる
Kafka Connect:Iceberg Sink Connectorを使ってみるKafka Connect:Iceberg Sink Connectorを使ってみる
Kafka Connect:Iceberg Sink Connectorを使ってみるMicroAd, Inc.(Engineer)
 
Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話
Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話
Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話MicroAd, Inc.(Engineer)
 
Chromeの3rd Party Cookie廃止とインターネット広告への影響
Chromeの3rd Party Cookie廃止とインターネット広告への影響Chromeの3rd Party Cookie廃止とインターネット広告への影響
Chromeの3rd Party Cookie廃止とインターネット広告への影響MicroAd, Inc.(Engineer)
 
ベアメタルで実現するSpark&Trino on K8sなデータ基盤
ベアメタルで実現するSpark&Trino on K8sなデータ基盤ベアメタルで実現するSpark&Trino on K8sなデータ基盤
ベアメタルで実現するSpark&Trino on K8sなデータ基盤MicroAd, Inc.(Engineer)
 
InternetWeek2022 - インターネット広告の羅針盤
InternetWeek2022 - インターネット広告の羅針盤InternetWeek2022 - インターネット広告の羅針盤
InternetWeek2022 - インターネット広告の羅針盤MicroAd, Inc.(Engineer)
 
マイクロアドにおけるデータストアの使い分け
マイクロアドにおけるデータストアの使い分けマイクロアドにおけるデータストアの使い分け
マイクロアドにおけるデータストアの使い分けMicroAd, Inc.(Engineer)
 
データセンターネットワークの構成について
データセンターネットワークの構成についてデータセンターネットワークの構成について
データセンターネットワークの構成についてMicroAd, Inc.(Engineer)
 
インフラ領域の技術スタックや業務内容について紹介
インフラ領域の技術スタックや業務内容について紹介インフラ領域の技術スタックや業務内容について紹介
インフラ領域の技術スタックや業務内容について紹介MicroAd, Inc.(Engineer)
 
RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例MicroAd, Inc.(Engineer)
 
アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜
アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜
アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜MicroAd, Inc.(Engineer)
 
アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜MicroAd, Inc.(Engineer)
 
アドテクに機械学習を組み込むための推論の高速化
アドテクに機械学習を組み込むための推論の高速化アドテクに機械学習を組み込むための推論の高速化
アドテクに機械学習を組み込むための推論の高速化MicroAd, Inc.(Engineer)
 
マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜
マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜
マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜MicroAd, Inc.(Engineer)
 
アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜MicroAd, Inc.(Engineer)
 
RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例MicroAd, Inc.(Engineer)
 
社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -
社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -
社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -MicroAd, Inc.(Engineer)
 
Digdagを用いた大規模広告配信ログデータの加工と運用
Digdagを用いた大規模広告配信ログデータの加工と運用Digdagを用いた大規模広告配信ログデータの加工と運用
Digdagを用いた大規模広告配信ログデータの加工と運用MicroAd, Inc.(Engineer)
 
これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~
これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~
これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~MicroAd, Inc.(Engineer)
 
インターネット広告の概要とシステム設計
インターネット広告の概要とシステム設計インターネット広告の概要とシステム設計
インターネット広告の概要とシステム設計MicroAd, Inc.(Engineer)
 

More from MicroAd, Inc.(Engineer) (20)

20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用
20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用
20240229 DEIM2024 【技術報告】広告配信における安定して拡張性のある大量データ処理基盤の必要性と活用
 
Kafka Connect:Iceberg Sink Connectorを使ってみる
Kafka Connect:Iceberg Sink Connectorを使ってみるKafka Connect:Iceberg Sink Connectorを使ってみる
Kafka Connect:Iceberg Sink Connectorを使ってみる
 
Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話
Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話
Apache Kafkaでの大量データ処理がKubernetesで簡単にできて嬉しかった話
 
Chromeの3rd Party Cookie廃止とインターネット広告への影響
Chromeの3rd Party Cookie廃止とインターネット広告への影響Chromeの3rd Party Cookie廃止とインターネット広告への影響
Chromeの3rd Party Cookie廃止とインターネット広告への影響
 
ベアメタルで実現するSpark&Trino on K8sなデータ基盤
ベアメタルで実現するSpark&Trino on K8sなデータ基盤ベアメタルで実現するSpark&Trino on K8sなデータ基盤
ベアメタルで実現するSpark&Trino on K8sなデータ基盤
 
InternetWeek2022 - インターネット広告の羅針盤
InternetWeek2022 - インターネット広告の羅針盤InternetWeek2022 - インターネット広告の羅針盤
InternetWeek2022 - インターネット広告の羅針盤
 
マイクロアドにおけるデータストアの使い分け
マイクロアドにおけるデータストアの使い分けマイクロアドにおけるデータストアの使い分け
マイクロアドにおけるデータストアの使い分け
 
データセンターネットワークの構成について
データセンターネットワークの構成についてデータセンターネットワークの構成について
データセンターネットワークの構成について
 
インフラ領域の技術スタックや業務内容について紹介
インフラ領域の技術スタックや業務内容について紹介インフラ領域の技術スタックや業務内容について紹介
インフラ領域の技術スタックや業務内容について紹介
 
RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例
 
アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜
アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜
アドテクを支える基盤 〜10Tバイト/日のビッグデータを処理する〜
 
アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜
 
アドテクに機械学習を組み込むための推論の高速化
アドテクに機械学習を組み込むための推論の高速化アドテクに機械学習を組み込むための推論の高速化
アドテクに機械学習を組み込むための推論の高速化
 
マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜
マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜
マイクロアドのデータ基盤について アドテクを支える基盤〜10Tバイト/日のビッグデータを処理する〜
 
アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜アドテクを支える技術 〜1日40億リクエストを捌くには〜
アドテクを支える技術 〜1日40億リクエストを捌くには〜
 
RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例RTBにおける機械学習の活用事例
RTBにおける機械学習の活用事例
 
社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -
社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -
社内問い合わせ&申請・承認業務の 管理方法 - Jira Service Management 事例紹介 -
 
Digdagを用いた大規模広告配信ログデータの加工と運用
Digdagを用いた大規模広告配信ログデータの加工と運用Digdagを用いた大規模広告配信ログデータの加工と運用
Digdagを用いた大規模広告配信ログデータの加工と運用
 
これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~
これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~
これから機械学習エンジニアとして戦っていくみなさんへ ~MLOps というマインドセットについて~
 
インターネット広告の概要とシステム設計
インターネット広告の概要とシステム設計インターネット広告の概要とシステム設計
インターネット広告の概要とシステム設計
 

Scala、DDD、Akkaで立ち向かう 〜広告配信システムに課せられた100msの制約〜

  • 2. 2 ● 撮影OKです ● スライドは公開します ● 色々な話を詰め込んでるので 興味ある所だけ聞いてもらえたら幸いです ● 時間足りなくて端折るスライドあると思いますの で、懇親会で話しましょう!
  • 3. 自己紹介 ● 名前: 松宮 康二(まっつー) ● 最近触ってるもの ● 最近の趣味 ○ ラーメン作り ● Twitter ○ @mattsu6666 3
  • 4. もくじ ● 広告配信システムの宿命 (5分) ● なぜScala, DDD, Akkaなのか (14分) ● 性能面の具体的な工夫 (10分) ● 可読性を維持する方法 ←スライド作ってから35分の発表では収まらない事に気付いた ● まとめ (1分) 4 割とボリュームが多くなってしまったので 駆け足気味で進みますm(__)m
  • 6. 100msの制約 ● SSPがDSPへ入札要求をしてから100ms以内に返却することが必須条件 ● 100msを超えた場合はタイムアウトで入札が拒否される ● レイテンシを含む時間なので現実的には遅くても30ms程度で処理する ● 入札できない = 落札されない = 売上を生まない = 倒産 6 100msの制約を守ることは業務が成立する必要条件 SSP DSP A DSP B DSP C WEBページ 広告枠 入札要求 100ms以内に応答しないとタイムアウト 応札(50ms) 広告をリクエスト 広告をレスポンス 応札(100ms) 応札(150ms) 入札要求 入札要求 50msで応答してい るので入札成功 100msで応答して いるので入札成功 150msで応答して いるので入札失敗
  • 7. 1つのエンドポイントに全ての機能が詰まっている ● 一般的なWebアプリ等と異なり、エンドポイントはSSP毎に1つだけ ○ 設計次第だがマイクロアドではSSP毎にエンドポイントを分けている ○ 1つのAPIに対して機能追加し続けるイメージ ● ビジネスのスケールとともに機能は増え続ける ● 機能が増えても100msの制約は不変 ● しかし機能追加は避けては通れない 7 機能追加は業務を継続する必要条件 SSP A DSP 入札要求 POST /rtb/a SSP B 入札要求 POST /rtb/b SSP C 入札要求 POST /rtb/c 位置情報を使う機能 ユーザの性別とか判定する機能 ユーザと似たユーザを検索する機能 行動履歴を使う機能 ブラックリストユーザ判定機能 ヽ(・ω・ヽ*) PDM オフライン購買データを使う機能 新機能追加して! RTBの内部処理 ・・・ 機能追加をすればする程、RTBの内部処理は 複雑かつ高負荷になっていく。
  • 8. 増え続ける広告在庫 ● ビジネスのスケールとともに扱う広告在庫は増大 ※広告在庫とは広告の表示可能回数のこと ● 1度のRTBで出来るだけ多くの広告を配信対象にしたい ● 多くの広告在庫を扱える = より精度の高い広告を配信可能 = 高い広告効果 ○ また、たくさん表示可能なため単純に売上が伸びる ● 一方で扱う広告の増大は処理負荷の増大に直結 ● 少なくても業務は成り立つけど、スケールしない 8 多くの広告在庫を扱えることは業務が成立する十分条件 SSP DSP 入札要求 応札 化粧品関連のページから リクエスト うーん・・・とりあえず健 康関連? DSP 化粧品が最適!(略) 入札要求 応札 DSP(略) 入札要求 応札 (タイムアウト) 化粧品 関連 健康 関連 旅行 関連 広告在庫 健康 関連 旅行 関連 広告在庫 旅行 関連 健康 関連 化粧品 関連 広告在庫 ・・・ お、おおすぎる! 処理が間に合わねぇ 広告在庫が少ないと・・・ 広告在庫が多いと 広告在庫が多過ぎると 処理が間に合わず機会損失が発生するのは もったいない! こうならないようにエンジニアが頑張る
  • 9. 増え続けるリクエスト ● ビジネスのスケールとともに受けるリクエストは増大 ● 出来る限り多くのリクエストを受けたい ● 大量のリクエスト = 入札機会の増大 = 売上アップ ● 大量のリクエストを捌きたいなら・・・ ○ サーバを増やす ○ サーバのスペックを上げる ● しかし・・ ○ サーバは高い(1台辺り10~20万) ○ ラックに限りがある ○ 1台辺りの収益率が落ちるのは本末転倒 ● よって、富豪的アプローチで万事解決とは行かない ● ハードに頼り切るのではなく、ソフトウェアも限界までチューニング 9 大量のリクエストが捌けることは業務が成立する十分条件 日毎のリクエスト数 ※軸の値は伏せてます 増加傾向にある
  • 10. まとめると ● 100msの制約を守ることは業務が成立する必要条件 ● 機能追加は業務を継続する必要条件 ● 多くの広告在庫を扱えることは業務が成立する十分条件 ● 大量のリクエストが捌けることは業務が成立する十分条件 10 このようなアドテク特有の課題をどうやってクリアするか? 100msの制約 機能追加 多くの広告在庫を扱える 大量のリクエストが捌ける 業務
  • 11. 業務課題に対するアプローチとキーワード ● プログラミング言語にScalaを採用 ○ 関数型プログラミング ○ オブジェクト指向プログラミング ○ JVM言語 ● ソフトウェアの設計手法にドメイン駆動設計(DDD)を採用 ○ 3層 + ドメインアーキテクチャ ○ ドメインモデル ○ ユビキタス言語 ● 主要なフレームワーク・ツールキットにAkkaを採用 ○ 並行並列処理 ○ リアクティブシステム ○ アクターモデル 11 次のスライドから詳細な説明をしていきます
  • 13. Scala ● JVM言語の1つ ● 静的型付け言語 ○ 型推論・パターンマッチング ● 小規模なスクリプトから大規模システムの構築まで幅広く利用可能 ○ Scalaはscalable languageに由来する ● すべてのJava製ライブラリと相互運用可能 ● オブジェクト指向と関数型プログラミングの概念を持つ ● 充実したコレクションAPI ○ ループ処理ではなく述語関数によって表現 13 object Main extends App { println("Hello world") } Seq(1,2,3,4,5) .map(_ * 2) .filter(_ > 5) > Hello world > Seq[Int] = List(6, 8, 10) Hello worldの例 述語関数を使った例(for文を使わずにループ処理)
  • 14. なぜScalaなのか ● 関数型言語の特徴を持っており、シンプルに記述できる ○ イミュータブル ○ 副作用を発生させづらい構文(例えばif文ではなくif式) ○ nullを発生させないデータ構造 ● オブジェクト指向言語の特徴を持っており、DDDとの相性が良い ○ ポリモーフィズム(最も重要!) ※後述 ○ カプセル化 ○ 継承 ● JVM言語の安心感 ○ 十分な実績を持つJavaとの互換性 ○ JVM上で動作するため、Javaに近い性能を期待できる ○ コンパイル言語なので、型安全による安心感 14
  • 15. 関数型言語はなぜシンプルなのか ● 関数型プログラミングは純粋関数だけを使う ○ 純粋関数とは副作用がない関数 ● 副作用とは関数内で副次的に発生するアクション ○ グローバル変数に再代入する ○ データベースを参照・更新する ○ I/O ○ 例外を投げる等 ● 純粋関数は入力に対して必ず同じ出力がある ● システムの状態に依存しないので... ○ 頭の中で状態を追いながら読まなく良い ○ テストコードが書きやすい 15 class Auth { public void auth(Token token) { token.set(getAuth()) } } 副作用があるケース(コードはJava) class Auth { def auth(token: Token): Token = { token.copy(getAuth()) } } 副作用がないケース 返り値(結果型)がvoidかTokenの違いがある 副作用があるケースは「 tokenを更新」 副作用がないケースは「 tokenを新規作成」
  • 16. ポリモーフィズムが重要な理由 ● ポリモーフィズム(多態性)はインタフェースに実装できる性質 ○ オブジェクト指向以前もポリモーフィズムは可能だったが、言語レベルで安全に実装できるようになった (だからこそ重要) ● インタフェースによってプログラムをより抽象的・汎用的に記述できる ○ 例えばUNIXのIOデバイスドライバはopen, close, read, write, seekの標準機能を提供する ○ もしインタフェースが統一されていなかったら、デバイス依存のコードになり用途が限られる ● インタフェース同士が依存し合うことで高いモジュール性・疎結合性を得る ● ポリモーフィズムによって、ソースコードの依存関係を絶対的に制御(※後述) 16 モジュール同士が抽象に依存 具象クラスを自由に付け替えられる => 疎結合 UNIXはデータの入出力をファイルとして扱う => デバイス非依存
  • 17. DDD ● DDD(ドメイン駆動設計)はソフトウェア設計手法のこと ○ 技術では無く、ドメイン知識(業務知識)を中心に考える ● 事業価値を最大化することが我々の究極の目標 ● しかし実際には... ○ ドメインエキスパート(PDMなどドメインに詳しい人)は事業価値の向上に注力 ○ エンジニアは業務上の問題を技術的に解決しようとする ○ エンジニアによって翻訳されたソフトウェアになってしまう ○ 両者のコミュニケーションの相違が起こってしまう ● DDDはドメインエキスパートとエンジニアが共通認識(ユビキタス言語)を 持ったソフトウェアを作る(コードがドメインモデルを反映) 17 (・ω・*) PDM 「広告」には「画像」とか「動画」とかの種類があるんだよ なぁ。 「広告主」がどっちか選んで「 登録」するんだよねぇ エンジニア (,,゚Д゚) 画像とか動画とかいってもフォーマット色々あるし、「 JPEG 広告」「PNG広告」「MP4広告」みたいな感じで実装すっか。 生成ロジックを統一したいし、「 ユーザ」が「広告ジェネレー タ」を使って「生成」するようにしよっと。 下の会話では両者が違う言葉を使っている。 この状況が続くと、両者の認識のズレは拡大し、いつかプロ グラムの修正が困難な状況に陥る可能性が高い
  • 18. ドメインモデルをピュアにすべきな理由 ● ドメインモデルは何にも依存させるべきではない(ピュアにすべき) ● さらに、ビジネスルールは技術的関心事に左右されるべきではない ● ビジネスルールとはドメインモデルの構成要素 ○ "Business rules describe the operations, definitions and constraints that apply to an organization. Business rules can apply to people, processes, corporate behavior and computing systems in an organization, and are put in place to help the organization achieve its goals." ○ つまり、ビジネス活動の中で意思決定を行うための規則 ○ 個人的には、コンピュータが消滅しても失われないルールのことだと思ってる ○ 僕らが一番守りたいのはビジネスルール 18 ※広告の例だと 分かりづらかっ たので お店を例にしま した ECサイト ~インターネット時代~商品を買う例 お店 ~近代~ お店 ~昔~ ● 合計の求め方 ● 小計の求め方 ● 消費税の求め方 などは「会計システム」「POSシステ ム」「そろばん」(技術的関心事)に左 右されるべきじゃない! 引用: https://en.wikipedia.org/wiki/Business_rule
  • 19. 巨大なモノリシックなアプリのメンテナビリティ ● 広告配信システムは非機能要件的にどうしてもモノリシックな一枚岩に ● 一枚岩故にドメインはめちゃくちゃ複雑 ● 一枚岩なのに色んなシステムと接続しまくる ● 何も考えずに作ったらメンテナビリティは容易に悪化 ● DDDを取り入れ多層アーキテクチャによってドメインモデルを保護 ● レイヤーの依存関係を厳格にし、影響範囲の局所化と明確化 19 プレゼンテーション層 データソース層 アプリケーション層 ドメイン層 A B AはBに依存している HTTPやRPCなどの入力の インタフェースを担当 ユースケースを担当 (ビジネスロジック) DB接続周り担当 ビジネスルール担当 (ドメインモデル)
  • 20. Akka ● Scala製の並行並列処理に特化したツールキット(フレームワークとライブラリを合わせたやつ) ● 主にLightbend社によって開発・メンテされているOSS ● 並行並列、分散システムを簡単に記述するためにアクターモデルを提供 ○ アクターモデル自体は1973年に登場 ○ クラウドコンピューティングの進歩とムーアの法則の終焉などでスケールアウトが主流になってきたことで 再び注目を浴びてきた ● アクター以外にAkka Streams, Akka Clustring, Akka HTTP等様々なコンポーネントを提供している ● Akkaはリアクティブ宣言に基づいて設計されている ○ 「リソースを効率よく利用し、アプリケーションをを自動的にスケールできるようにすること」 (Akka実践バイブルより引用) 20 引用: https://www.reactivemanifesto.org/ja
  • 21. アクターモデル ● アクターはメッセージ駆動の計算実体(スレッドに似ている) ○ アクターは非同期に実行される ● アクター同士(数千・数百万)が協調して1つのアプリケーションを構成する ● アトミックやロックを使わず、安全に並行並列処理が実装できる ○ アクターはメールボックス(メッセージキューみたいな)を持っていて、1度に1つ処理 ● アクターはfire-and-forget(撃ちっぱなし)の性質によって、非同期処理を実現している ● スケーラビリティのためにアクター同士は疎結合になっている ○ 空間/位置: 位置透過性とも。同じノード、異なるノード等、どこにアクターがいても良い ○ 時間: アクターはタスクの完了について何も保証しないし、期待もしない ○ インターフェース: アクター同士は何も共有しない。インタフェースが正しいかとか関係ない 21 Actor Actor Actor メッセージ送信 メッセージ送信 メッセージ送信 アクターシステム メールボックス (メッセージキュー) アクターは非同期で動作するのでア クターの数だけ並行並列に動作可 能 ロックとかアトミックな操作は一切不 要 メッセージを受信し て何か処理
  • 22. スループットではなくレイテンシを高める ● アクターモデルによって、並行並列処理が安全に実装可能 ● スレッドでは実現出来なかったスケールアウトも可能 ● アクターによって処理を細かい単位で分離するので、局所的にスケール可能 ● よって、Akkaを採用することにした ● しかし・・・良いことばかりではない ○ 高い学習コスト ○ アプリケーションの性質上、コアなロジックはアクターによるスケールアウトが難しい(通信コストが無視出来ない) ○ まだ発展途上のOSSのため、バージョン上がるとガッツリAPIが変わってる ● スループットは変わらないが、レイテンシは高めたい(アクターモデルの恩恵を享受) ○ 全体をそれなりに早くではなく、1リクエスト辺りの応答速度を高めたい ○ 大量のリクエストを満遍なくタイムアウトさせるより、少量のリクエストを確実に返せた方が良い ○ そのためにはアクターモデルを活用した並行並列処理が使いやすい 22 リクエスト1の処理 リクエスト2の処理 リクエスト1の処 理 リクエスト1の処 理 リクエスト2の処 理 リクエスト2の処 理 スレッド1 スレッド2 200ms スレッド1 スレッド2 200ms 200msでどちらも2件のリクエストを 捌いた。 しかし、左は200msかかってしまっ てタイムアウトになった 一方で右は100msで応答したので 入札できた 広告配信システムでは100msの制 約があるのでタイムアウト! 超理想の話なので実際にはこんな 綺麗に並列化は難しい
  • 23. なぜScala, DDD, Akkaなのか ● Scala ○ オブジェクト指向、関数型のメリットを享受できる ○ JVM上で動作するので性能面での安心感 ○ 強力な型システムによって規模の拡大が原因でメンテナビリティが低下しづらい等 ● DDD ○ 複雑なドメインに立ち向かう手段 ○ ビジネスルールに集中でき、事業価値を高めやすい ○ Scalaと相性が良い(オブジェクト指向との相性が良い) ● Akka ○ 並行並列処理、分散処理を安全に実装できる ○ 様々な便利なコンポーネントが使える 23
  • 25. オンメモリ戦略 ● アプリケーションのボトルネックの大部分はI/O ○ 通信コスト ○ データを検索するコスト ● 載せれるデータはローカルのメモリ上に持てるだけ持つ ○ いわゆるオンメモリ戦略 ● オンメモリ化の条件 ○ ある程度の更新間隔を許容(数分~数十分は古くても良い) ○ 件数が膨大過ぎない(メモリに載る容量か) ○ 揮発しても良いデータ(どこかで永続化されてる必要がある) ● 以上の条件を満たせばメモリに載せる ● 単純な戦略だが最も効果的 ● ただし、メモリには限界があるので無敵ではない 25 アプリ DB キャッシュ 定期的にDBのデータをキャッシュ アプリ DB 毎回DBに問い合わせるので遅い 常に最新のデータを取得できるが、 本当にその必要がある? 速度を犠牲にしてまで最新データにこだわるべき か? 最新のデータは使えないが、ローカルのメモリに キャッシュするので高速。 ただし、データの容量等の様々な制限はある 引用: https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html メインメモリの参照は100ns SSDのランダムリードは16μs ≒ 16000ns 単純計算で160倍の速度差 さらに、ネットワークのレイテンシを考えればオン メモリ化が如何に効果的かわかる
  • 26. 整合性の担保 ● オンメモリ戦略を使った場合の考慮点として整合性がある ● いくら古いデータを許容できるといっても、過去の異なる時点のデータを参照するのは不味い ● そこで、IOモナドを利用してデータの取得と更新タイミングを分離(更新だけ遅延させる) ○ IOモナドって何・・? => 関数合成出来て、遅延評価が可能な性質が重要。厳密な定義は知りません。 ● DBへの問い合わせを先にやり、全てのデータが揃った時点で、一括でオンメモリのデータを差し替える ● これによってほぼ整合性を担保できる(厳密にはDBの問い合わせタイミングが若干ズレるがそこは許容) ● 万一DBの問い合わせが一部失敗した場合は、全てロールバックする ● ただし、一時的に使用するメモリが本来保持するデータ量の2倍になる 26 アプリ DB A 新キャッシュ DB B DB C キャッシュ アプリ 新キャッシュ キャッシュ各種DBからデータを取得していく が、全てのデータが揃うまで、既 存のキャッシュは更新しない 全てのデータが揃ったタイミングで既存のキャッ シュを新キャッシュで置き換える (参照が切り替わるだけ) またキャッシュ参照時にはリエントラントロックを かける
  • 27. リアルタイムなデータはインメモリデータベース ● オンメモリ戦略を適用できないデータはどうするか? ○ リアルタイム性が求められるデータ ○ ローカルのメモリには乗り切らないデータ ● リモートホストのインメモリデータベースにキャッシュする ○ 通信は発生するがデータは高速で読める ○ マイクロアドでは主にRedisを利用 (NoSQLの一つ、KVSとも) ● Redisを活用することで、リアルタイムにデータを高速に読み込めるがKVSの特徴を理解した設計が必要 ● 効率良くデータを取得するために、アプリケーションに特化したスキーマで構成する ● 例えばユーザに行動履歴が紐づく場合は単なるString型(Key:Value形式)よりもHash型の方が効率が良い ○ レコードに複数のデータを含む場合は、MessagePack等で事前に圧縮しておく 27 user1_action1 user1_action2 user1_action3 user2_action1 user2_action2 http://aaa.com http://bbb.com http://ccc.com http://aaa.com http://bbb.com key value user1 user2 http://aaa.com http://bbb.com http://ccc.com http://aaa.com http://bbb.com key value action1 action2 action3 field action1 action2 String型 Hash型 キーを単純にしたことで検索性能が改善
  • 28. 本質でない処理を切り離す ● 広告配信システムの本領は入札要求に対して入札すること ○ しかし、それ以外にも色々やっている(ログの書き出し, DBの更新, 他サービスとの連携等) ● 例えば、入札は出来る限り高速に応答したいが、ログの書き出しは多少遅くても良い ● 本質でない処理にCPUのリソースを割くのは勿体ない。そこでアクターモデルを活用 ○ fire-and-forgetの性質によって応答を待つ必要はない ○ アクターは位置透過性なのでログの書き出しはリモートホストで処理してもいい ● ただし、メッセージが到達する信頼性は「at-most-once」 ○ つまり、メッセージは喪失する可能性があるので重要なメッセージは送れない ○ at-least-onceを保証する方法も一応可能。ただアクターモデルの旨味が消える 28 RTB処理はログ記録の応答を一切待たないの でログ記録の影響を受けない ログ記録がリモートホストに存在する場合、ログ は喪失する可能性がある ログの重要度によって、対応方法を変える必要 がある ←ログが消滅!!! 重要なログだったらヤバイ・・・
  • 30. 統一したコードフォーマット ● チーム開発ならコードフォーマットの統一は絶対した方がいい ● みんながバラバラのフォーマッタを使っていると、無駄なコード修正が入って本質を見落とす可能性 ● ScalaならScalafmtが良さげ ○ 他にもscalariformとかIntellijの設定を共有する方法もある ○ まぁお好みでって感じだが、体感Scalafmtが一番使われてそう 30 object ScalafmtTest extends { val aaa = "MicroAd" def bbb(): Int = { val a = 1 val b = 2 a + b } } Scalafmt前 object ScalafmtTest extends { val aaa = "MicroAd" def bbb(): Int = { val a = 1 val b = 2 a + b } } Scalafmt 後
  • 31. レイヤー構造に基づいたクラス設計 ● レイヤーの構成方法は三者三様 ○ レイヤードアーキテクチャ, クリーンアーキテクチャ, オニオンアーキテクチャ, ヘキサゴナルアーキテクチャ, 3層 + ドメインモ デル等 ○ 色々あるし、独自にカスタマイズしても問題無い ● 一番重要なのはドメインモデルをピュアにすること ● そして、レイヤーの依存関係をないがしろにしないこと ○ ScalaならsbtのdependsOnを利用するとコンパイラレベルで強制可能 ● レイヤー構成を定めると修正箇所を局所化できる ● また、設計の指針になるため、人によって実装方法がブレ辛い 31 プレゼンテーション層 データソース層 アプリケーション層 ドメイン層 HTTPやRPCなどの入力の インタフェースを担当 ユースケースを担当 (ビジネスロジック) DB接続周り担当 ビジネスルール担当 (ドメインモデル) ドメイン層は誰にも依存していない。 つまり、ドメイン層以外が修正されてもドメイン 層には何も影響がない。 何も影響がない状況を保ち続けるのが超重 要!!!
  • 32. インタフェースに依存させる ● ポリモーフィズムを活用してクラス同士の抽象度を高く保つ ● 抽象的であればある程、変更に柔軟なコードになる ● 「依存関係逆転の原則」を利用すると依存関係は思いのまま 32 インフラ層 ドメイン層 Repository Repository Impl Domain Service Repository Impl ドメイン層からインフラ層は 参照してはいけない インタフェースをドメイン層に置けば 依存関係を保ったまま参照できる
  • 33. まとめ ● 広告配信システムの成立条件のお話をした ○ 100msの制約を守ることは業務が成立する必要条件 ○ 機能追加は業務を継続する必要条件 ○ 多くの広告在庫を扱えることは業務が成立する十分条件 ○ 大量のリクエストが捌けることは業務が成立する十分条件 ● 以上の成立条件を満たすためにScala, DDD, Akkaによるアプローチを紹介した ○ Scalaは「関数型言語」、「オブジェクト指向言語」、「JVM言語」の視点から ○ DDDは「複雑なドメインに立ち向かう手段」として ○ Akkaは「並行並列処理を安全に実装」するため ● そして「性能面の具体的な工夫」として、マイクロアドで実践している内容を挙げた ○ オンメモリ戦略 ○ 整合性の担保 ○ リアルタイムなデータはインメモリデータベース ○ 本質でない処理を切り離す ● 加速性を維持する方法ではDDDを実践していくコツを紹介した ※今回はしてない 33
  • 35. 参考文献 ● Scalaスケーラブルプログラミング第3版 ● Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関 数型徹底ガイド ● Clean Architecture 達人に学ぶソフトウェアの構造と設計 ● 実践ドメイン駆動設計 ● Akka実践バイブル アクターモデルによる並行・分散システムの実現 ● 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向 の実践技法 35