Developers Summit 2014 Summer 【B-4】LMQでお手軽分散システム開発

Post on 12-Nov-2014

1.473 views 1 download

Tags:

description

夏サミ2014 【B-4】LMQでお手軽分散システム開発のスライドです

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: lmq@127.0.0.1Active nodes: lmq@127.0.0.1

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 lmq@lmq1.example.com

クラスタから離脱

$ 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