【参加レポ】Akka ActorとAMQPでLINEのメッセージングパイプラインをリプレースした話【LINE DEVELOPER DAY_2015】 #linedevday
4/28に開催されたLINEのエンジニアチームの様々な経験を、未解決の課題も含めて共有する技術カンファレンス、
に参加してきたのでその時のまとめ。
メッセージングパイプラインについてのお話です。
ムズイッス…
他のセッションは以下からどうぞ
Yuichi Ono様
概要
LINEのメッセージングは人と人とやるのと、人とシステムとでやるものの2種類ある
公開型のアカウントから一般の人に送るときは20億メッセージおくったりする
一斉送信を解決するためにつくったのがメッセージングパイプライン
- システムに対してメッセージを送るときは、Featcherが受信キューに送ってPusherが外部システムにおくる
- 公開アカウントが送るときは送信キューにいれて、FanOutというサービスが一斉に送る
- 受信キューはRabbitMQを使っている
受信キューについて
- メッセージングの記法要件として送信した順序で受信側に到達しなければならない
- 普通MQベースで使うときはひとつのキューに対して複数のコンシューマをわりあてるが、処理順が保証されていないので、1キューに対して1コンシューマでやらないといけない
- 1Q1Cの問題
- コンシューマのフェイルオーバーをどう実現するか
- 1キューに対して正確に1コンシューマーを割り当てる方法
補足:AMQPのACKの仕組みについて
AMQPのACKはコンシューマに応じて必ずメッセージを処理するという仕組み
ACKが来なかった場合は別のコンシューマにメッセージを送信する
1Q1Cのフェイルオーバー方法
AMQP ACKを活用して1Q1Cのフェイルオーバーを編み出した!!
コーディネーションキューにコンシューマーの名前を書いたキューをいれておく
肝は死ぬまでackを返さないようにすることで1Q1Cを実現できた
- コンシューマがダウンしたらメッセージをダウンしたコンシューマに即座に送り直す
結果として、RabbitMQのみでフェイルオーバー可能に。またスケーラビリティも上がった
スループットを改善したPusherについて
- フェイルオーバーを考慮した1Q1Cの実装
- ライブラリの持ってる再接続機能だけじゃできないので自前で実装しないといけない
- 送信元アカウントごとにメッセージをバッファリングしないと順序保証ができない
- 計算リソースを完全に使い切りつつ、限界を超えないようにリアルタイム処理の実現 – 宛先システムの遅延が全体に影響を及ばさないようにスレッディングしないといけない
前はmicro batchという少しずつ処理するようにしていたが、stream batch処理するようにした
アクターモデル
- アクターはスレッドやロックの代わりに使うもので、それぞれが独立したスレッドないしプロセスでうごいており、それらがメッセージを非同期に行うことで並行処理をモデル化する
- 送ったメッセージはアクターのメールボックスに入り、送られたアクターはそのメールボックスに入った処理を処理していく
- アクター自身はシングルスレッドで記述できるのでロック処理を書かなくていい
Akkaについて
- Akkaはアクターモデルを実現するためのScalaおよびJavaむけの並行プログラミングライブラリ
- 重要なこと1:ステートマシンの実装が簡単
- ReceiveFunctionの切り替えが可能
- いったん状態遷移図かいてからステートマシンにおとしこむことができた
- 重要なこと2:Supervisorにより、エラー処理とデータフローをきりわけられることができる
Pusher
Pusherの中身
- RabbitMQがメッセージキューとコンディションキューを管理する
- ここのアクターがコンシューマとして動く
一旦中継用のアダプターを書いて、次に1つのAMQPアクターにすべて集約している
AMQPのライブラリ自体に再接続の機能があるが、複数のコネクション管理だと、逆に実装しづらくなるので、集約することで簡潔にかけた
AMQP ActorはFlow Control Actorにわたしている
- アクターのインプットとアウトプットさえ合っていれば様々なパイプライン処理ができる
処理がおわったらAckをきた順序に返していく
- バッファーサイズは気をつけないといけない
- BackPressureというのが必要になる
- 各アクターに各バッファーのサイズを監視させて、Aというバッファーがたまっていたら、A宛のメッセージングをとめてという通知をするようにしている
- 問題なくなったら送信していいよという通知も行う
Metrics Actorのなかでサンプリングなどをして外部に送信を行ったりしている
7秒単位に、アクター別、サーバー別にメトリックスをみれるようになったりしている
結果として
- フロントにRabbitMQを建てることでActorの仕組みで再送信できるようになった
FanOutについて
複数のアカウントに一斉に送信することがある -> ラウンドロビンアルゴリズムを採用した
メッセージのフローを一旦ためて、カーソルを順番に操作して順番にメッセージを送信している
結果として
- ラウンドロビンアルゴリズムのおかげでQoSの目標を実現!
おわりに
非同期技術スタックを検討すべき理由
- マイクロサービスの採用により、サービス間通信の重要性が高まった
- どんな技術を使うのかが大事になってきた
- 同期RPCの罠は、連鎖的障害を残しやすい
- 同期RPCを通じてシステム全体に連鎖的に影響をおよぼす
- ウェブサービスの時代からスマートフォン・ウェアラブルの時代へ
- アプリの比重が増えたことで、ユーザーの生活にサービスが深く入り込むようになった
- 同期処理では賄えなくなってきている
- 非同期処理をうまく抽象化して並行処理を簡単にかける環境は整ってきているので新しい武器として使おう!
LINEの注力分野
所感
マルチスレッドプログラミングについての知識が恐ろしく乏しいのを感じたため、少し勉強したほうがいいのではという危機を感じた。
もしマルチスレッドプログラミングするときはアクターモデルを試してみます!!