drb09
description
Transcript of drb09
dRuby and Rinda.
その導入編を松江で関将俊
Agenda
私について
dRubyの紹介
演習
Rinda (時間があれば)
重要なことを先に
先に
重要
まだ初刷買えます!
dRubyによる
関 将俊 著
分散・Webプログラミング
私について
地理的な関係
しごと関係
Rubyとの関係
栃木県那須塩原市
初サンライズ出雲
東北新幹線→サンライズ出雲→やくも
移動を甘く見てた
自分も周りも興奮(?)して眠れない
しごと
サラリーマン
自称アーティスト
ちゃんとした会社員
わりと大きな組織
製品のためのプログラミング
eXtreme Programmingだったもの
ソフトウェアの品質系学会
多くの場合、悪役として招待される
自称アーティスト
自分のため・自己中心
アーティストとしてのプログラマ
わかって欲しい人にウケるため
オープンソースへの愛や正義には興味がない
Rubyと私
20世紀の終わり頃から
ERB, dRuby, Rinda, RWiki, ....
2000年のPerl/RubyConference
書籍・雑誌
ERB
文書にRubyスクリプトを埋め込む
Railsの実習で触った?
非常にこだわりがあるライブラリ
しかし利用者にはほとんど伝わらない
dRuby and Rinda.
今日のテーマです
今日は
dRubyの基本的な機能を紹介し、実際に動かして実験します
細かいどうでもよい点(しかし私がていねいに設計した点)にはなるべく触れません
雰囲気を味わってください
今日は
短いプログラムを写経
一度にたくさんのプログラムを起動します
やったことありますか?
RMI
Remote Method Invocation
別のプロセス/マシンのオブジェクトにメッセージを送り、メソッドを起動する仕組み
いったい何がすごいの
プロセスの境界
OSによって保護された空間
プロセスの壁を越えるにはどうする?
サーバ
誰かに奉仕するプロセス
たとえばWebサーバ
それが本業であるプログラム
そうなるべく最初から設計されてる
お願いを待ち受ける仕組を持つ
ふつうは
聞く耳を持たないプロセス
dRubyは
そんなプロセスにも待ち受ける仕組みを提供する
みんなをサーバに
Ruby風
Webサービスとの違い
dRubyはRubyに特化している
徹底的にRubyの常識に従う
演習
1台のマシンの中の複数のプロセスが協調して動く様子を体験します
演習1
Hello, World.
いくつかの約束事を覚えます
演習1
hello_server.rb hello_client.rbhello()
"Hello, World."
hello_server.rb
require 'drb/drb'
class Hello def hello puts('Hello, World.') endend
DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end
require
require 'drb/drb'
class Hello def hello puts('Hello, World.') endend
DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end
DRb.start_service
require 'drb/drb'
class Hello def hello puts('Hello, World.') endend
DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end
URI
require 'drb/drb'
class Hello def hello puts('Hello, World.') endend
DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end
終了させない
require 'drb/drb'
class Hello def hello puts('Hello, World.') endend
DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end
本来は sleep でよいいくつかの環境向けのトリック
約束事
require 'drb/drb'
DRb.start_service
URI
終了させない
hello_client.rb
require 'drb/drb'
DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello
hello_client.rb
require 'drb/drb'
DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello
hello_client.rb
require 'drb/drb'
DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello
hello_client.rb
require 'drb/drb'
DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello
約束事
require 'drb/drb'
DRb.start_service
DRbObject.new_with_uri
require 'drb/drb'
DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello
require 'drb/drb'
class Hello def hello puts('Hello, World.') endend
DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end
元はこれ
class Hello def hello puts('Hello, World.') endend
ro = Hello.newro.hello
演習1
hello_client.rbをなんども実行できますか?
hello_server.rbを終了させてhello_client.rbを実行させるとどうなりますか?
使いどころ
空間
一つのプロセスの扱える量を超える
時間
プロセスの寿命を超える (例 CGI)
機能による分割
演習2
共有されたHashにいろいろ出し入れしてみる
複数のプロセスがオブジェクトを交換する様子を感じて
KVS...
require 'drb/drb'
DRb.start_service('druby://localhost:54300', Hash.new)while true sleep 1end
演習2
hash_server.rb
irbirb
演習2
どんなオブジェクトが入るか試して
String, Integer, IO
オブジェクトの交換
それでもRuby風になりきれない部分
交換するのはコピーか参照か
引数、戻り値、例外
Rubyでは参照のやりとりしかないよ
オブジェクトの交換
def hoge(a1, a2, a3, &block)...raise FooError unless some_cond...return value
end
受け取ったものは何か
意識したくない
Rubyだし
リモートのオブジェクトならdRubyがRMIしてくれる
複製注意
でもOOPなら複製しないよな‥
送るものは何か
意識したくない
Rubyだし
すべてを参照にすべきか?
Rubyだからね
でもいつか値が必要になる
値を送るか、参照を送るか
dRubyの戦略
オブジェクトの交換方法を勝手に選択
Marshal可能かどうかが条件
こんなの
Marshal
dumpablepass by value
Number, String, Boolean...
can’t dumppass by reference
Proc, Thread, File ...
本当にこの選択でよいのか
わかんないけどわりと実用的
気に入らなければ明示的に参照を選べる
手間だけど値渡しを選ぶこともできる
カスタマイズしたdumpが必要
演習3
待ち行列を使った同期
RubyのQueue
スレッド同期の仕組み
非同期と同期
それぞれ勝手に動くプログラムを協調させる(同期)
本当に関係ないプログラムが勝手にうごく場合には非同期とは呼ばない(と思う)
Queueは待合せと情報の交換を同時に行う
Queue
FIFOバッファ
pushとpop
空のときにpopするとブロックする
データが届いたらまた動き出す
Queue
Queue
スレッドの視点に立つと‥
「待つ」のは取り出す係
並べるのはオブジェクト
並んで待つのではないことに注意
Queue
演習3
データがない間、停まってるか?
irb Queue deque.rb
require 'drb/drb'
DRb.start_servicequeue = DRbObject.new_with_uri('druby://localhost:54320')while true p queue.pop sleep(rand)end
require 'drb/drb'require 'thread'
DRb.start_service('druby://localhost:54320', Queue.new)while true sleep 1end
Queueからデータを一つpopして印字するランダムに少しsleepする
演習3
deque.rbを増やしたらどうなる?
irb Queue deque.rb
deque.rb
deque.rb
演習4
Queueに似てるなにか
データには順序がある
全部読み切ってしまったらブロック
何度でも読める
Twitter!
1 "Hello, World."
2 "Hello, Again."
3 :Three
4 4
5 [5]
好きなところから開始
1 "Hello, World."
2 "Hello, Again."
3 :Three
4 4
5 [5]
順に読める
1 "Hello, World."
2 "Hello, Again."
3 :Three
4 4
末尾でブロック
1 "Hello, World."
2 "Hello, Again."
3 :Three
4 4
5 [5]
追加されると再開
1 "Hello, World."
2 "Hello, Again."
3 :Three
4 4
5 [5]
再び末尾でブロック
notify.rb
26行!!
今日、最長のプログラム
require 'rinda/tuplespace'require 'drb/drb'
class Notifier def initialize @ts = Rinda::TupleSpace.new @ts.write([:tail, 0]) end
def notify(it) tuple = @ts.take([:tail, nil]) tail = tuple[1] + 1 @ts.write([tail, it]) @ts.write([:tail, tail]) tail end
def receive(key) @ts.read([key, nil]) endend
DRb.start_service('druby://localhost:54321', Notifier.new)while true sleep 1end
class Notifierclass Notifier def initialize @ts = Rinda::TupleSpace.new @ts.write([:tail, 0]) end
def notify(it) tuple = @ts.take([:tail, nil]) tail = tuple[1] + 1 @ts.write([tail, it]) @ts.write([:tail, tail]) tail end
def receive(key) @ts.read([key, nil]) endend
Rinda::TupleSpaceへの操作で実現されている
RindaとLinda
Lindaは並列処理糊言語
タプルとタプルスペース
パターンマッチング
Lindaの協調
out
inとrd
パターンで指定
ブロックできる
かっこいい並列処理
TupleSpace
Engine
Engine
Engine
Client
Client
Tuple Spaceだけを知っている
Rinda
RubyによるLindaの実装
dRubyを意識してくれる
TupleはArray|Hashで
Tuple
[:chopstick, 2][:room_ticket][‘abc’, 2, 5][:matrix, 1.6, 3.14][‘family’, ‘is-sister’, ’Carolyn’, ’Elinor’]
Pattern
• Tupleの要素と===で比較する
• case equalsなのでパターンっぽく動く
• Regexp, Range, Classとか
• nilはワイルドカード
Pattern
[/^A/, nil, nil][:matrix, Numeric, Numeric][’family’, ’is-sister’, ’Carolyn’, nil][nil, ’age’, (0..18)]
[’seki’, ’age’, 20]
>> require ‘rinda/tuplespace’>> ts = Rinda::TupleSpace.new >> ts.write([’seki’, ’age’, 20]) >> ts.write([’sougo’, ’age’, 18]) >> ts.write([’leonard’, ’age’, 18]) >> ts.read_all([nil, ’age’, 0..19]) => [["sougo", "age", 18], ["leonard", "age", 18]] >> ts.read_all([/^s/, ’age’, Numeric]) => [["seki", "age", 20], ["sougo", "age", 18]]
writeとtake
>> ts.write([:seki, :age, 20])=> #<Rinda::TupleEntry:...>>> TupleSpace
:seki :age
write
20
writeとtake
>> ts.write([:seki, :age, 20])=> #<Rinda::TupleEntry:...>>>
>> ts.take([:seki, :age, nil]) => [:seki, :age, 20]>>
TupleSpace
:seki :age
take
20
演習4の仕組み
二種類のタプルによる同期
登場するタプルclass Notifier def initialize @ts = Rinda::TupleSpace.new @ts.write([:tail, 0]) end
def notify(it) tuple = @ts.take([:tail, nil]) tail = tuple[1] + 1 @ts.write([tail, it]) @ts.write([:tail, tail]) tail end
def receive(key) @ts.read([key, nil]) endend
末尾を示すタプル
順序とデータのタプル
:tail 0
initialize
:tail 0
notify("Hello, World.")
:tail 0
take([:tail, nil]) [:tail, 0]が取り除かれる
1 "Hello, World."
write([tail, it])末尾に要素を追加
:tail 1
1 "Hello, World."
write([:tail, tail])末尾のindexをメモ
:tail 1
1 "Hello, World."
receive(1)
:tail 1
1 "Hello, World."
read([1, nil])[1, "Hello, World.]のコピーが返る
:tail 1
1 "Hello, World."
receive(2)
:tail 1
1 "Hello, World."
read([2, nil])[2, nil]にマッチするタプルがないのでブロックする
:tail 1
1 "Hello, World."
notify("Hello, Again.")
:tail 1
1 "Hello, World."
take([:tail, nil]) [:tail, 1]が取り除かれる
1 "Hello, World."
2 "Hello, Again."
write([tail, it])
:tail 2
1 "Hello, World."
2 "Hello, Again."
write([:tail, tail])
:tail 1
1 "Hello, World."
2 "Hello, Again."
read([2, nil])[2, nil]にマッチしたので再開しこのタプルが返る
receive.rb
require 'drb/drb'
DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54321')
key = 0while true key, obj = ro.receive(key + 1) p [key, obj] sleep(3)end
添字を増やしながら受信して印刷するループ
演習4
古いデータを削除するなら、どんな仕組みを用意すればよいと思いますか?
重要なことをもう一度
もう一度
重要
まだ初刷買えます!
dRubyによる
関 将俊 著
分散・Webプログラミング
まとめ
まだ初刷買えます。
dRubyの雰囲気を味わえた?
ふりかえり