AP4R加藤 究 & 篠原 俊一
2008. 2. 13
1
はじめに
2
•むかし、土木専攻•少し前、メッセージングミドルウェアの保守 / 拡張
kiwamu
3
•Ruby は 2006 年から
•いま、AP4R の開発
•http://d.hatena.ne.jp/kiwamu/
kiwamu
4
shino•ぷろぐらまー、
コーディング大好き !
•今年の言語は...C
5
•オープンソースの世界へ•http://d.hatena.ne.jp/ita-wasa
•日曜物理学者、超ひも理論
shino
6
会社紹介• フューチャーアーキテクト株式会社• http://www.future.co.jp/index.html
• 本社: 大崎オフィス
• 設立: 1989.11.28
• 社員数: 連結 1,007名、単体 663名 (2007.12.31 時点)
• 売上: 連結 250億18百万円、単体 160億46百万円7
8
9
10
発表の流れ1. 概説
2. 「軽量」について
3. デモ
4. 「堅牢」について
5. その他
6. まとめ11
1. 概説
12
大事なことは先に
13
1-1. AP4R とは何か ?
1-2. ユースケース
1-3. AP4R の強みは ?
1-4 事例14
AP4R とは何か?
15
AP4R とは何か ?
•Ruby のメッセージングライブラリ
•Asynchronous Processing for Ruby
•非同期処理による疎結合化
16
•社内開発してきたメッセージングミドルウェア
•“RtFA”
AP4R の開発背景
17
•Pure Java
•独自のプロトコルと API (≠ JMS)
•さまざまなシステム上での実績
RtFA
18
http://www.future.co.jp/company/news/061227.html
“RtFA” の最近の事例
19
•1日1億件以上の貨物データ
•並列実行•負荷分散
プロジェクト概要
20
•約 100 台のサーバ群
•AP サーバに “RtFA” 採用
プロジェクト概要
21
http://www.oreilly.co.jp/books/9784873113203/
From Java to Ruby
22
From RtFA to AP4R
•Java での経験をもとに、よい部分を継承しつつ、進化
•とくに使いやすさ (API, 設定 etc)
23
オープンソース•2006 年 OSS 化 (MIT)
•コミュニティとともに成長•エンタープライズ分野への適用
24
AP4R のユースケース
•ユーザーへの素早い応答•負荷分散
25
ユーザーへの素早い応答
26
すぐに応答の必要がない処理は、
あとで実行
27
Synchronously
Client Server
Sequential tasks
28
Synchronously
Client Server
A B C D E
Sequential tasks
28
Client Server Messaging Server
Better solution
29
Client Server Messaging Server
Better solution
A B
29
Client Server Messaging Server
Asynchronously
A B
30
Client Server Messaging Server
Asynchronously
A B C D E
30
典型的な用途
•メール送信•重いサマリデータの作成•システム間連携
31
負荷分散
32
スケールアウトのしやすさ
Client Server Messaging Server
33
Client Server Messaging Servers
スケールアウトのしやすさ
34
AP4R の強みは ?
35
「軽量」 かつ
「堅牢」36
軽量って ?
37
空気のような存在
38
•動的•簡潔•オープンクラス•メタプログラミング ...etc
Ruby の強み
39
AP4R で実現•使いやすさ
• シンプルな API と設定
• Rails とのシームレスな連携
•All in one support• ライブラリだけでなく、
• テストや運用のための仕組みも提供40
「軽量」 一つめの強みは、
41
堅牢って ?
42
ポストマンの意地
43
メッセージの配送保障
44
メッセージの喪失や二重処理の危険性
45
•注文したのに商品が届かない...
•代金が二重に引き落とされてる!
46
メッセージは
信頼できる方法で配送しないといけない
47
異常時こそ真価•データベース障害•N/W 障害
•サーバダウン•ビジー状態、タイムアウト
... etc48
「堅牢」 二つめの強みは、
49
AP4R の強みは...
50
「軽量」 かつ
「堅牢」51
堅牢軽量
シンプルな API
テストサポート
SAF
リカバリ
柔軟なシステム構成
52
事例
53
•オンラインゲーム
•Interactivemediums.com
54
オンラインゲーム
55
概要
•イギリス•Web ベースの戦略ゲーム
•未公開、現在 β テスト中
56
• メール送信• レポート作成• データ分析• データベースアクセスを含む、
CPU インテンシブな処理
AP4R の利用用途
57
規模•専用 AP4R サーバ × 2
•Rails プロセス × 4 / AP4R
•1時間に数百メッセージ
•MySQL に永続化58
Interactivemediums.com
59
概要•アメリカ•モバイル向けサービス、
TextMe for Business ( http://textmeforbusiness.com )
•2007年7月より稼動中60
AP4R queues
Textprocessing
inbound
outbounddelivery
acceptance
通知サービス on SMS
deliveryprocessing
routing
delivery
Third Partyapplication
62
なぜ、AP4R を利用したのか?
63
• 「複数サーバ上で、負荷分散しやすい」• 「スケールさせやすい」• 「活発な開発」• 「メッセージングの経験ゆたかな
開発チーム」
アンケート結果
64
Coffee break
65
:Firefox =>
(c) 2007 Mozilla Japan66
:Firefox =>
(c) 2007 Mozilla Japan
Foxkeh
67
マスコット重要
69
:AP4R => ...
70
Copyright © 2007 by Kanican
Licensed under Creative Commons License.
71
起源>“AP4R”.sub(/4/, ”A”)
>“APAR”.downcase
>“apar”
>ミツオビアルマジロ
Dictionary:http://www.alc.co.jp/Photo:http://www.flickr.com/photos/jeffclow/29738818/
72
Licensed under Creative Commons License.
Copyright © 2007 by Kanican
73
Copyright © 2007 by Kanican
74
Maro75
閑話休題
76
発表の流れ1. 概説
2. 「軽量」について
3. デモ
4. 「堅牢」について
5. その他
6. まとめ77
2. 「軽量」について
78
堅牢軽量
シンプルな API
テストサポート
SAF
リカバリ
柔軟なシステム構成
79
シンプルな API
80
メッセージの PUT
81
ap4r.async_to({destination}, {data} [,{options}])
ブロック形式でも記述可能c.f. RDoc: http://ap4r.rubyforge.org/doc/
82
Class AsyncShopController < ApplicationController
def order # synchronous part o = Order.new(:name => params[:name]) o.save! ap4r.async_to({:action => ‘payment’}, {:order_id => o.id})
redirect_to ... end
def payment # asynchronous part Payment.new(:order_id => params[:order_id]).save! ... render :text => ‘true’ end
end83
User Apache Rails AP4R
84
User Apache Rails AP4R
order
message put
85
Class AsyncShopController < ApplicationController
def order # synchronous part o = Order.new(:name => params[:name]) o.save! ap4r.async_to({:action => ‘payment’}, {:order_id => o.id})
redirect_to ... end
def payment # asynchronous part Payment.new(:order_id => params[:order_id]).save! ... render :text => ‘true’ end
end86
User Apache Rails AP4R
order
message put
87
User Apache Rails AP4R
dispatch88
User Apache Rails AP4R
payment
89
Class AsyncShopController < ApplicationController
def order # synchronous part o = Order.new(:name => params[:name]) o.save! ap4r.async_to({:action => ‘payment’}, {:order_id => o.id})
redirect_to ... end
def payment # asynchronous part Payment.new(:order_id => params[:order_id]).save! ... render :text => ‘true’ end
end90
柔軟なシステム構成
91
典型的な構成
92
User Apache Rails AP4R
93
メッセージング層の負荷分散
94
User Apache Rails AP4R
95
同期と非同期処理でサーバを分離
96
User Apache Rails AP4R
URL rewrite
97
User Apache Rails AP4R
URL rewriteHTTP
97
User Apache Rails AP4R
URL rewriteHTTP
98
プロトコル•デフォルト: HTTP POST
•対応済み:
• SOAP
• XML-RPC
• dRuby99
流量制御
100
処理数 処理時間
Task “busy” 多い 短い
Task “heavy” 少い 長い
2 種類の処理
101
URL rewrite
heavy
busy
102
設定ファイル dispatchers: - targets: queue.very_busy.* threads: 10 modify_rules: url: proc {|url| url.host = "busy.async.host"} - targets: queue.very_heavy.* threads: 1 modify_rules: url: proc {|url| url.host = "heavy.async.host"}
103
堅牢軽量
シンプルな API
テストサポート
SAF
リカバリ
柔軟なシステム構成
104
3. デモ
105
•買い物アプリ•注文処理と決済処理
概要
106
最初Client Server
A
order
B
payment
107
AP4R 導入Client Rails AP4R Rails × 3
108
注文処理は同期でClient Rails AP4R Rails × 3
A
order
109
決済処理は非同期でClient Rails AP4R Rails × 3
A B
order payment
110
滞留メッセージの監視Client Rails AP4R Rails × 3
0255075
100
111
•アプリを簡単に非同期化•負荷をかけても応答はすぐに•着々と処理されるメッセージ
デモの流れ
112
113
114
発表の流れ1. 概説
2. 「軽量」について
3. デモ
4. 「堅牢」について
5. その他
6. まとめ115
4. 「堅牢」について
116
堅牢軽量
シンプルな API
テストサポート
SAF
リカバリ
柔軟なシステム構成
117
Stored And Forward
118
業務処理のトランザクションと
メッセージ生成の
一貫性を保障119
CRUD
commit
message put
app-DB AP4RRails msg-DB
insert /commit
SAF create
SAF delete
Store
Forward
✓120
Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions by Gregor Hohpe,Bobby Woolf
Guaranteed Delivery [122]
121
SAF 詳細
122
非同期送信メッセージが生成されるまでの流れ
123
start
end
app-DB AP4RRails
124
CRUDstart
end
app-DB AP4RRails
124
CRUD
commit
start
end
app-DB AP4RRails
124
CRUD
commit
message put / commit
start
end
app-DB AP4RRails
124
CRUD
commit
message put / commit
start
end data
app-DB AP4RRails
124
CRUD
commit
message put / commit
start
end data messages
app-DB AP4RRails
124
業務処理のトランザクションと
メッセージ生成の
一貫性を保障したい
125
•エラーが起きた場合は、•メッセージは生成されない
データベース更新で...
126
•正常に終了した場合は、•メッセージは必ずキューに送られる
データベース更新で...
127
安心☺
128
「軽量」 かつ
「堅牢」129
Ap4r::Client#transaction Class ShopController < ApplicationController def order # synchronous side ap4r.transaction do o = Order.new(:name => params[:order_id]) o.save!
ap4r.async_to({:action => ‘payment’}, {:order_id => o.id})
end redirect_to ... end end
130
キューイングの一連の処理が変ります
131
ap4r.transaction do ... # CRUD on database
ap4r.async_to(...)
end
ap4r.transaction の裏側
132
ap4r.transaction do ... # CRUD on database
ap4r.async_to(...)
end
ap4r.transaction の裏側
Insert into SAF table
132
ap4r.transaction do ... # CRUD on database
ap4r.async_to(...)
end
ap4r.transaction の裏側
Insert into SAF table
Commit database
132
ap4r.transaction do ... # CRUD on database
ap4r.async_to(...)
end
ap4r.transaction の裏側
Insert into SAF table
Commit databaseQueue messages
132
ap4r.transaction do ... # CRUD on database
ap4r.async_to(...)
end
ap4r.transaction の裏側
Insert into SAF table
Commit databaseQueue messages
Store
132
ap4r.transaction do ... # CRUD on database
ap4r.async_to(...)
end
ap4r.transaction の裏側
Insert into SAF table
Commit databaseQueue messages
Store
Forward
132
SAF がない場合
133
CRUD
commit
message put / commit
start
end data messages
134
CRUD
commit
message put / commit
start
end data messages
134
CRUD
commit
message put / commit
start
end data messages
135
CRUD
commit
message put / commit
start
end data messages
135
CRUD
commit
message put / commit
start
end data messages
135
CRUD
commit
message put / commit
start
end data messages
136
CRUD
commit
message put / commit
start
end data messages
136
CRUD
commit
message put / commit
start
end data messages
136
CRUDstart
end data messages
counterchangecommit
message put / commit
137
CRUD
commit
message put / commit
start
end data messages
counterchange
138
CRUD
commit
message put / commit
start
end data messages
139
CRUD
commit
message put / commit
start
end data messages
139
CRUD
commit
message put / commit
start
end data messages
140
CRUD
commit
message put / commit
start
end data messages
140
CRUD
commit
message put / commit
start
end data messages
140
CRUD
commit
message put / commit
start
end data messages
141
CRUD
commit
message put / commit
start
end data messages
141
CRUD
commit
message put / commit
start
end data messages
141
障害時こそ真価•データベース障害•N/W 障害
•サーバダウン•ビジー状態、タイムアウト
... etc142
異常系重要☺
☠
143
SAF がある場合
144
CRUD
commitmessage put / commit
start
end data messages
SAF create
SAF update
145
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
146
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
146
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
147
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
147
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
147
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
☺147
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
147
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
148
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
148
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
149
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
149
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
149
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
☺149
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
150
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
150
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
151
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
151
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
messages
151
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
messages
Recoverable
151
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
☺messages
Recoverable
151
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
152
commitmessage put / commit
SAF update
CRUDstart
end data messages
SAF create
152
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
153
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
messages
153
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
☺messages
153
message put / commitcommit
SAF update
CRUDstart
end data messages
SAF create
☺messages
Duplicated
153
業務処理のトランザクションと
メッセージ生成の
一貫性を保障154
プロデューサとチャネル間で
最低一回
at-least-once155
堅牢軽量
シンプルな API
テストサポート
SAF
リカバリ
柔軟なシステム構成
156
5. その他
157
•障害時のメッセージのリカバリ•Capistrano (2.x) との連携
•テストサポート
158
•Functional テスト• 通信なし
• 速い、けど制限あり
•Async テスト• 通信あり
• 遅い、けど現実に近い
2 種類のテストを提供
159
gihyo.jp 特集
• AP4R、Ruby で非同期メッセージング
•全 4 回 (2007.9.6 - 9.27)
• http://gihyo.jp/dev/feature/01/ap4r
160
テストサポート詳細
161
TDD / BDD
162
安心できるアプリ開発
☺
163
非同期アプリでは
•テストは同じく重要、でも...
•N/W をまたいだ複数のプロセス
• 時間がかかる• セットアップが面倒
164
•Functional テスト• 通信なし
• 速い、けど制限あり
•Async テスト• 通信あり
• 遅い、けど現実に近い
2 種類のテストを提供
165
Functional テスト
•キューのスタブを提供•メモリ上で実行、速い
166
コード例このテストコードは架空のオンラインショッピングアプリのものです。同期で実行される注文処理と、非同期で実行される決済処理とがあります。
167
def test_order post :order, :order => {:item => "introduction to AP4R"} assert_response :redirect assert_redirected_to :action => 'index'
messages = @controller.ap4r.queued_messages # ... (1) assert_equal 1, messages.keys.size, "should have messages in just ONE queue" assert messages.key?("queue.async_shop.payment"), "queue name is INCORRECT" # ... (2) assert_equal 1, messages["queue.async_shop.payment"].size, "should have just ONE message for payment" assert_match /order_id=\d+/, messages["queue.async_shop.payment"].first[:body], "parameter order_id should be included with a numeric value" # ... (3) end
1. キューのスタブからメッセージを取得2. キューの名前を assert
3. メッセージの中身を assert168
CRUD
message put
functional test
Stub
assert
assert
rake ..
169
Async テスト
•現実に近い状態で•すべてをテスト• e.g. シリアライゼーション、メッセージ連携
170
コード例このテストコードは架空のオンラインショッピングアプリのものです。同期で実行される注文処理と、非同期で実行される決済処理とがあります。
171
ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") require "ap4r/service_handler" ap4r_test_helper = Ap4r::ServiceHandler.new require 'test_help' class Test::Unit::TestCase self.use_transactional_fixtures = false self.use_instantiated_fixtures = false # Add more helper methods to be used by all tests here... cattr_accessor :ap4r_helper def ap4r_helper @@ap4r_helper end def with_services(&block) ap4r_helper.with_services(&block) end end
Test::Unit::TestCase.ap4r_helper = ap4r_test_helper
[RAILS_ROOT]/test/async/ap4r_test_helper.rb
172
require "#{File.dirname(__FILE__)}/ap4r_test_helper"require 'net/http'
class AsyncShopTest < Test::Unit::TestCase
def test_http_dispatch ap4r_helper.stop_dispatchers # ... (1)
assert_rows_added(Order, 1) { # ... (3) do_order # ... (2) } assert_rows_added(Payment, 1) { # ... (6) ap4r_helper.start_dispatchers # ... (4) ap4r_helper.wait_all_done # ... (5) } end
private # Requests to <tt>async_shop/order</tt>. def do_order(item_name = "test item") Net::HTTP.start("localhost", 3000, nil, nil) do |http| http.request_post("/async_shop/order", "order[item]=#{item_name}") do |res| #nop end end end
def assert_rows_added(model, rows) rows_before = model.count yield rows_after = model.count assert_equal rows, rows_after - rows_before, "table '#{model.table_name}' should count up by #{rows}" endend
[RAILS_ROOT]/test/async/async_shop_test.rb
173
1. dispatcher スレッドを停止2. HTTP POST で Rails にリクエスト3. orders テーブルに一行追加されたのを assert
4. dispatcher スレッドを開始5. 非同期のアクションが完了するのを待つ6. payment テーブルに一行追加されたのをassert
174
CRUD
message put
async test
HTTP
rake ..
assert
service control
assertCRUD
HTTP
175
•補完的な2つのテストで
• Functional テスト
• Async テスト
•安心して非同期アプリ開発
テストサポート
176
6. まとめ
177
AP4R は ..
178
「軽量」 かつ
「堅牢」179
堅牢軽量
シンプルな API
テストサポート
SAF
リカバリ
柔軟なシステム構成
180
•シンプルな API と設定で使えるメッセージング
•Rails とのシームレスな連携
•柔軟なシステム構成が可能181
•dispatcher による非同期処理の自動実行
•SAF によるメッセージ配送保障
•テストサポート
182
今後の展望
183
円滑な運用 ver.0.3.x
☑デーモン化
☑ URL 書き替えフィルタ
☑ DLQ / SAF リカバリ
□プロトコル拡充: Stomp, HTTP
184
監視機能強化 ver.0.4.x
□ OSS 監視ツール Cacti,
ZABBIX などとの連携
□複数 AP4R プロセスの管理
□対話型管理ツール185
大規模対応へステップ ver.0.5.x
□ 動的設定□ 自動リカバリ□ 連続無停止稼動
186
その他□ セキュリティ☑テストサポート
□ ブロッキングキュー□ Ruby off Rails
187
Your patches are welcome!Thank you.
AP4R@RubyForge
•wiki: http://ap4r.rubyforge.org/wiki/wiki.pl?HomePage
• source: svn co http://ap4r.rubyforge.org/svn/trunk/ap4r
•ML: http://rubyforge.org/mailman/listinfo/ap4r-user
by Kanican188
• Future Architect logo belongs to Future Architect, Inc. Japan. All rights reserved.
• Rails logo is trademarks of David Heinemeier Hansson. All rights reserved.
• The Ruby logo is copyrighted (c) 2006, Yukihiro Matsumoto. It is released under the terms of the Creative Commons Attribution-ShareAlike 2.5 License.
189
Top Related