Developers Summit 2014 Summer 【B-4】LMQでお手軽分散システム開発
-
Upload
developers-summit -
Category
Technology
-
view
1.473 -
download
1
description
Transcript of Developers Summit 2014 Summer 【B-4】LMQでお手軽分散システム開発
LMQ でお手軽分散システム開発Developers Summit 2014 Summer@yosisa
自己紹介
— 田中 義久— Software Engineer
— Go / Python / Erlang / Swift
— Internet Initiative Japan Inc.
— Twitter: @yosisa
— GitHub: @yosisa
— Qiita: @yosisa
社内での開発スタイル
— 新監視システム開発チーム— メンバー6人— GitHub Enterprise
— 内製の CI サーバ— Docker / GCE
LMQLightweight Message Queue
https://github.com/iij/lmq
LMQメッセージキュー written in Erlang
— プロセス間のデータの受け渡しに使う— REST 風の HTTP API で Put / Get
— キューなのでデータを保持する
LMQ の目指すところ
— 使いやすい— 運用しやすい— 十分に速い— メッセージをロストしない
開発の動機
より良い監視システムを作る
旧来の監視システム (1)
監視データをローカルに格納している。
例えば、N 回連続で監視失敗したらアラートをあげる設定の時、ローカルにある過去の監視データが無いと正確な判定ができない。
一度割り当てた監視ターゲットを別ホストに移すのが難しい。
旧来の監視システム (2)
監視の系を二重化して障害に備えている。
通知が二重に出るのを防ぐために、集約処理をするプログラムがある。
旧来の監視システム (2)
複数のアラートが起きた時に、通知が多数でるのを防ぐために、30秒~10分間の間でアラートを集約している。
集約プログラムにデータが流れてくると、再起動ができなくて、メンテナンスが難しい。
旧来の監視システム (3)
監視・解析・アラート担当と、集約・通知担当の二段構成。
✘監視の生データを別の用途に使う✘システムの一部だけリプレースする
旧来の監視システム (4)
もう8年くらい動いてる。
つまり、ベースのコードは相当古い。
テスト?なにそれおいしいの?
建て増しに次ぐ建て増しでカオス。
亜種がたくさんあってカオス。
Perl と C で書かれてて\(^o^)/
まとめると
もうこれ以上
面倒みたくない!
\(^o^)/
— メンテナンスがすごく大変— 設計や実装が古くて時代にそぐわない— デプロイが職人芸と化していて、デプロイしたくないからコード書きたくない
— でも新たな要求はやってくる
でも新たな要求はやってくる
なら
作るしかないじゃない
新監視システム
毎分 10万 IPアドレスを監視できて、
監視ホストを追加するだけでスケールして、プロセスの再起動に気を遣わなくてよく、
容易に機能追加できる拡張性のある、
近代的な開発手法で作られた、
そんな監視システム
おわかりいただけただろうか?
大規模監視システムを支える
LMQ
LMQ の特徴
LMQ の特徴
— HTTP REST API
— シングル構成 / 冗長構成が可能— 名前をつけて複数のキューを作成できる— キューの自動生成— タイムアウトベースのメッセージ再送— 時間ベースのメッセージ集約
Erlang/OTP の特徴
— 関数型言語— 軽量プロセスモデル— Shared Nothing
— プロセス間のメッセージパッシング— マルチコアを有効活用— ネットワーク系の機能が Built-in
RabbitMQ との違い
GettingStarted
インストール
Erlang/OTP R16B01 以上が必要$ git clone https://github.com/iij/lmq.git$ cd lmq$ make rel
起動
$ rel/lmq/bin/lmq start
動作確認
$ rel/lmq/bin/lmq pingpong
Basic API
Put APIPOST /messages/:name
キューにメッセージを追加する
POST /messages/greeting HTTP/1.1content-type: text/plain
Hello, world!
HTTP/1.1 200 OKcontent-type: application/json
{"accum": "no"}
Get APIGET /messages/:name
キューからメッセージを取り出す
GET /messages/greeting HTTP/1.1
HTTP/1.1 200 OKcontent-type: text/plainx-lmq-message-id: e93fd6b1-d408-4ecb-9f6b-d3eeebce34c1x-lmq-message-type: normalx-lmq-queue-name: greeting
Hello, world!
Reply APIPOST /messages/:name/:msgid?reply=:type
メッセージの処理結果を通知するtype は ack, nack, ext の3種類
— ack: 正常終了 -> メッセージを削除— nack: 継続不能 -> メッセージを戻す— ext: 処理に時間がかかっている
-> タイムアウトをリセット
Reply APIPOST /messages/:name/:msgid?reply=:type
POST /messages/greeting/e93fd6b1-d408-4ecb-9f6b-d3eeebce34c1?reply=ack HTTP/1.1
HTTP/1.1 204 No Content
Reply APIPOST /messages/:name/:msgid?reply=:type
POST /messages/greeting/e93fd6b1-d408-4ecb-9f6b-d3eeebce34c1?reply=ack HTTP/1.1
HTTP/1.1 404 Not Found
Multi Queue API
Multi Queue API
個々のキューを指定する代わりに、パターンにマッチする全てのキューを対象にする
API。
パターンは正規表現で指定する。
Put all APIPOST /messages?qre=:regexp
パターンにマッチする全てのキューにメッセージを追加する。
対象のキューはあらかじめ存在している必要がある。
Put all APIPOST /messages?qre=:regexp
POST /messages?qre=.* HTTP/1.1Content-Type: application/json; charset=utf-8
{"text": "added via multi queue api"}
HTTP/1.1 200 OKcontent-type: application/json
{ "greeting": {"accum": "no"}}
Get any APIGET /messages?qre=:regexp
パターンにマッチするいずれかのキューから取得
GET /messages?qre=.* HTTP/1.1
HTTP/1.1 200 OKcontent-type: application/json; charset=utf-8x-lmq-message-id: 7cdfc909-ae9c-4704-bfa2-fd99612103e8x-lmq-message-type: normalx-lmq-queue-name: greeting
{"text": "added via multi queue api"}
ステータス
ステータス確認
$ rel/lmq/bin/lmq-admin status All nodes: [email protected] nodes: [email protected]
greeting 1 messages 968 bytes accum: 0.0, retry: 2, timeout: 30.0
統計情報
$ rel/lmq/bin/lmq-admin statsgreeting push rate: 1min 0.00, 5min 0.00, 15min 0.02, 1day 0.20 pull rate: 1min 0.00, 5min 0.00, 15min 0.00, 1day 0.00 retention time: min 0.000, max 0.000, mean 0.000, median 0.000
StatsD / Graphite / InfluxDB
StatsD へのメトリクス送信に対応
Graphite や InfluxDB に統計情報を集約することが可能
タイムアウト
タイムアウト
LMQ はメッセージが Get されてからの時間を管理している。
timeout 値を超えると、そのメッセージが正しく処理できなかったとみなしてキューに戻す。
キューに戻ったメッセージは、他のクライアントから Get できるようになる。
メッセージの集約
メッセージの集約
一定時間内に特定のキューに Put された全てのメッセージを集約して、時間経過後に1つのメッセージとして Get できるようにする機能のこと。
複数のコンテンツを含む以外は、通常のメッセージと変わらない。
キューのプロパティを設定して有効化。
メッセージの集約
POST /messages/accum:seq HTTP/1.1Content-Type: application/json; charset=utf-8
{"id": 1}
HTTP/1.1 200 OKcontent-type: application/json
{"accum": "new"}
POST /messages/accum:seq HTTP/1.1{"id": 2}
HTTP/1.1 200 OKcontent-type: application/json
{"accum": "yes"}
メッセージの集約
GET /messages/accum:seq HTTP/1.1
HTTP/1.1 200 OKcontent-type: multipart/mixed; boundary=fdaef3d8-934e-4272-b886-3f3212942f3bx-lmq-message-id: fdaef3d8-934e-4272-b886-3f3212942f3bx-lmq-message-type: compoundx-lmq-queue-name: accum:seq
--fdaef3d8-934e-4272-b886-3f3212942f3bContent-Type: application/json; charset=utf-8
{"id": 1}
--fdaef3d8-934e-4272-b886-3f3212942f3bContent-Type: application/json; charset=utf-8
{"id": 2}
--fdaef3d8-934e-4272-b886-3f3212942f3b--
メッセージの集約
GET /messages/accum:seq?cf=msgpack HTTP/1.1
HTTP/1.1 200 OKcontent-type: application/x-msgpackx-lmq-message-id: e227d532-01e7-42a0-b1bc-bea8efc710e4x-lmq-message-type: compoundx-lmq-queue-name: accum:seq
����content-type�application/json; charset=utf-8�{"id": 1}���content-type�application/json; charset=utf-8�{"id": 2}
キューのプロパティ
キューのプロパティ
各キュー毎に動作をカスタマイズする仕組み。
— timeout: メッセージが再送されるまでの時間
— retry: メッセージの再送回数— accum: メッセージを集約する時間
デフォルトプロパティ
パターンにマッチするキューのプロパティをまとめて設定する仕組み。
大量のキューを使い分ける時に、個別に設定しなくてすむ。
パターンは正規表現で記述する。
Property API
Get Queue PropertyGET /properties/:name
GET /properties/greeting HTTP/1.1
HTTP/1.1 200 OKcontent-type: application/json
{ "accum": 0, "retry": 2, "timeout": 30}
Update Queue PropertyPATCH /properties/:name
PATCH /properties/greeting HTTP/1.1Content-Type: application/json; charset=utf-8
{"accum": 30}
HTTP/1.1 204 No Content
Reset Queue PropertyDELETE /properties/:name
DELETE /properties/greeting HTTP/1.1
HTTP/1.1 204 No Content
Get Default PropertiesGET /properties
GET /properties HTTP/1.1
HTTP/1.1 200 OKcontent-type: application/json
[ ["^accum:", {"accum": 30}]]
Set Default PropertiesPUT /properties
PUT /properties HTTP/1.1Content-Type: application/json; charset=utf-8
[ ["^accum:", {"accum": 30}]]
HTTP/1.1 204 No Content
Set Default PropertiesPUT /properties
GET /properties/accum:notify HTTP/1.1
HTTP/1.1 200 OKcontent-type: application/json
{ "accum": 30, "retry": 2, "timeout": 30}
Reset Default PropertiesDELETE /properties
DELETE /properties HTTP/1.1
HTTP/1.1 204 No Content
GET /properties HTTP/1.1
HTTP/1.1 200 OKcontent-type: application/json
[]
クラスタリング
クラスタリング
障害対策に2つ以上の LMQ ノードを使ってクラスタを組むことができる。
マスターレスの実装。
キューの操作毎に sync する。
クラスタリングクラスタに参加
$ rel/lmq/bin/lmq-admin join [email protected]
クラスタから離脱
$ rel/lmq/bin/lmq-admin leave
Performance
Single
Cluster
Demo
ユースケース
プロセス間の連携
プロセス間の連携
Producer と Consumer を疎結合にできる。
どちらのプロセスも、いつでも再起動できる。
Consumer をサーバのようにする必要がない。
Consumer のキャパシティを超えても、Producer に影響しない。
プロセス間の連携
Producer / Consumer を自由に増減できる。
Consumer を増やすだけで負荷分散できる。
PubSub 風
PubSub 風
Notifier は event:.* パターンを使って1度だけ Put すればよい。
Event Handler は
event:<hostname> から
Get する。
Event Handler が一時的に
down していても取りこぼさない。
バッチ処理
バッチ処理
流れてくるメッセージをある程度まとめてから処理できる。
1秒間まとめるだけでも、後段の処理が 60回/分 になり、バースト時の負荷を抑えられる。
ex: メール、IRC、ElasticSearch
Webhook Receiver
Webhook Receiver
hook handler を HTTP
Server にする必要がない。
handler の数を調整するだけで、直列処理 or 並列処理を選択できる。
handler が fail しても、hook 情報を失わない。
Desktop 通知
Desktop 通知
直接疎通がなくても、LMQ に疎通があるもの同士で通信できる。
NAT 配下や動的 IP 化にあることの多い Desktop 環境で、サーバからの通知を受け取れる。
まとめ
まとめ
LMQ はプロセスを役割毎に分割しやすくする。
LMQ を経由するポイントでシステムを拡張しやすくなる。
複雑なシステムを、がんばって運用しなくていい。
Thank youhttps://github.com/iij/lmq