drb09

98
dRuby and Rinda. その導入編を松江関将俊

description

Slide of lecture on Shimane University on December 4

Transcript of drb09

Page 1: drb09

dRuby and Rinda.

その導入編を松江で関将俊

Page 2: drb09

Agenda

私について

dRubyの紹介

演習

Rinda (時間があれば)

Page 3: drb09

重要なことを先に

先に

Page 4: drb09

重要

まだ初刷買えます!

dRubyによる

関 将俊 著

分散・Webプログラミング

Page 5: drb09

私について

地理的な関係

しごと関係

Rubyとの関係

Page 6: drb09

栃木県那須塩原市

Page 7: drb09

初サンライズ出雲

東北新幹線→サンライズ出雲→やくも

移動を甘く見てた

自分も周りも興奮(?)して眠れない

Page 8: drb09

しごと

サラリーマン

自称アーティスト

Page 9: drb09

ちゃんとした会社員

わりと大きな組織

製品のためのプログラミング

eXtreme Programmingだったもの

ソフトウェアの品質系学会

多くの場合、悪役として招待される

Page 10: drb09

自称アーティスト

自分のため・自己中心

アーティストとしてのプログラマ

わかって欲しい人にウケるため

オープンソースへの愛や正義には興味がない

Page 11: drb09

Rubyと私

20世紀の終わり頃から

ERB, dRuby, Rinda, RWiki, ....

2000年のPerl/RubyConference

書籍・雑誌

Page 12: drb09

ERB

文書にRubyスクリプトを埋め込む

Railsの実習で触った?

非常にこだわりがあるライブラリ

しかし利用者にはほとんど伝わらない

Page 13: drb09

dRuby and Rinda.

今日のテーマです

Page 14: drb09

今日は

dRubyの基本的な機能を紹介し、実際に動かして実験します

細かいどうでもよい点(しかし私がていねいに設計した点)にはなるべく触れません

雰囲気を味わってください

Page 15: drb09

今日は

短いプログラムを写経

一度にたくさんのプログラムを起動します

やったことありますか?

Page 16: drb09

RMI

Remote Method Invocation

別のプロセス/マシンのオブジェクトにメッセージを送り、メソッドを起動する仕組み

Page 17: drb09

いったい何がすごいの

プロセスの境界

OSによって保護された空間

プロセスの壁を越えるにはどうする?

Page 18: drb09

サーバ

誰かに奉仕するプロセス

たとえばWebサーバ

それが本業であるプログラム

そうなるべく最初から設計されてる

お願いを待ち受ける仕組を持つ

Page 19: drb09

ふつうは

聞く耳を持たないプロセス

Page 20: drb09

dRubyは

そんなプロセスにも待ち受ける仕組みを提供する

みんなをサーバに

Page 21: drb09

Ruby風

Webサービスとの違い

dRubyはRubyに特化している

徹底的にRubyの常識に従う

Page 22: drb09

演習

1台のマシンの中の複数のプロセスが協調して動く様子を体験します

Page 23: drb09

演習1

Hello, World.

いくつかの約束事を覚えます

Page 24: drb09

演習1

hello_server.rb hello_client.rbhello()

"Hello, World."

Page 25: drb09

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

Page 26: drb09

require

require 'drb/drb'

class Hello def hello puts('Hello, World.') endend

DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end

Page 27: drb09

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

Page 28: drb09

URI

require 'drb/drb'

class Hello def hello puts('Hello, World.') endend

DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end

Page 29: drb09

終了させない

require 'drb/drb'

class Hello def hello puts('Hello, World.') endend

DRb.start_service('druby://localhost:54000', Hello.new)while true sleep 1end

本来は sleep でよいいくつかの環境向けのトリック

Page 30: drb09

約束事

require 'drb/drb'

DRb.start_service

URI

終了させない

Page 31: drb09

hello_client.rb

require 'drb/drb'

DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello

Page 32: drb09

hello_client.rb

require 'drb/drb'

DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello

Page 33: drb09

hello_client.rb

require 'drb/drb'

DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello

Page 34: drb09

hello_client.rb

require 'drb/drb'

DRb.start_servicero = DRbObject.new_with_uri('druby://localhost:54000')ro.hello

Page 35: drb09

約束事

require 'drb/drb'

DRb.start_service

DRbObject.new_with_uri

Page 36: drb09

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

Page 37: drb09

元はこれ

class Hello def hello puts('Hello, World.') endend

ro = Hello.newro.hello

Page 38: drb09

演習1

hello_client.rbをなんども実行できますか?

hello_server.rbを終了させてhello_client.rbを実行させるとどうなりますか?

Page 39: drb09

使いどころ

空間

一つのプロセスの扱える量を超える

時間

プロセスの寿命を超える (例 CGI)

機能による分割

Page 40: drb09

演習2

共有されたHashにいろいろ出し入れしてみる

複数のプロセスがオブジェクトを交換する様子を感じて

Page 41: drb09

KVS...

require 'drb/drb'

DRb.start_service('druby://localhost:54300', Hash.new)while true sleep 1end

Page 42: drb09

演習2

hash_server.rb

irbirb

Page 43: drb09

演習2

どんなオブジェクトが入るか試して

String, Integer, IO

Page 44: drb09

オブジェクトの交換

それでもRuby風になりきれない部分

交換するのはコピーか参照か

引数、戻り値、例外

Rubyでは参照のやりとりしかないよ

Page 45: drb09

オブジェクトの交換

def hoge(a1, a2, a3, &block)...raise FooError unless some_cond...return value

end

Page 46: drb09

受け取ったものは何か

意識したくない

Rubyだし

リモートのオブジェクトならdRubyがRMIしてくれる

複製注意

でもOOPなら複製しないよな‥

Page 47: drb09

送るものは何か

意識したくない

Rubyだし

すべてを参照にすべきか?

Rubyだからね

でもいつか値が必要になる

値を送るか、参照を送るか

Page 48: drb09

dRubyの戦略

オブジェクトの交換方法を勝手に選択

Marshal可能かどうかが条件

Page 49: drb09

こんなの

Marshal

dumpablepass by value

Number, String, Boolean...

can’t dumppass by reference

Proc, Thread, File ...

Page 50: drb09

本当にこの選択でよいのか

わかんないけどわりと実用的

気に入らなければ明示的に参照を選べる

手間だけど値渡しを選ぶこともできる

カスタマイズしたdumpが必要

Page 51: drb09

演習3

待ち行列を使った同期

RubyのQueue

スレッド同期の仕組み

Page 52: drb09

非同期と同期

それぞれ勝手に動くプログラムを協調させる(同期)

本当に関係ないプログラムが勝手にうごく場合には非同期とは呼ばない(と思う)

Queueは待合せと情報の交換を同時に行う

Page 53: drb09

Queue

FIFOバッファ

pushとpop

空のときにpopするとブロックする

データが届いたらまた動き出す

Queue

Page 54: drb09

Queue

スレッドの視点に立つと‥

「待つ」のは取り出す係

並べるのはオブジェクト

並んで待つのではないことに注意

Queue

Page 55: drb09

演習3

データがない間、停まってるか?

irb Queue deque.rb

Page 56: drb09

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する

Page 57: drb09

演習3

deque.rbを増やしたらどうなる?

irb Queue deque.rb

deque.rb

deque.rb

Page 58: drb09

演習4

Queueに似てるなにか

データには順序がある

全部読み切ってしまったらブロック

何度でも読める

Twitter!

Page 59: drb09

1 "Hello, World."

2 "Hello, Again."

3 :Three

4 4

5 [5]

好きなところから開始

Page 60: drb09

1 "Hello, World."

2 "Hello, Again."

3 :Three

4 4

5 [5]

順に読める

Page 61: drb09

1 "Hello, World."

2 "Hello, Again."

3 :Three

4 4

末尾でブロック

Page 62: drb09

1 "Hello, World."

2 "Hello, Again."

3 :Three

4 4

5 [5]

追加されると再開

Page 63: drb09

1 "Hello, World."

2 "Hello, Again."

3 :Three

4 4

5 [5]

再び末尾でブロック

Page 64: drb09

notify.rb

26行!!

今日、最長のプログラム

Page 65: drb09

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

Page 66: drb09

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への操作で実現されている

Page 67: drb09

RindaとLinda

Lindaは並列処理糊言語

タプルとタプルスペース

パターンマッチング

Page 68: drb09

Lindaの協調

out

inとrd

パターンで指定

ブロックできる

Page 69: drb09

かっこいい並列処理

TupleSpace

Engine

Engine

Engine

Client

Client

Tuple Spaceだけを知っている

Page 70: drb09

Rinda

RubyによるLindaの実装

dRubyを意識してくれる

TupleはArray|Hashで

Page 71: drb09

Tuple

[:chopstick, 2][:room_ticket][‘abc’, 2, 5][:matrix, 1.6, 3.14][‘family’, ‘is-sister’, ’Carolyn’, ’Elinor’]

Page 72: drb09

Pattern

• Tupleの要素と===で比較する

• case equalsなのでパターンっぽく動く

• Regexp, Range, Classとか

• nilはワイルドカード

Page 73: drb09

Pattern

[/^A/, nil, nil][:matrix, Numeric, Numeric][’family’, ’is-sister’, ’Carolyn’, nil][nil, ’age’, (0..18)]

Page 74: drb09

[’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]]

Page 75: drb09

writeとtake

>> ts.write([:seki, :age, 20])=> #<Rinda::TupleEntry:...>>> TupleSpace

:seki :age

write

20

Page 76: drb09

writeとtake

>> ts.write([:seki, :age, 20])=> #<Rinda::TupleEntry:...>>>

>> ts.take([:seki, :age, nil]) => [:seki, :age, 20]>>

TupleSpace

:seki :age

take

20

Page 77: drb09

演習4の仕組み

二種類のタプルによる同期

Page 78: drb09

登場するタプル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

末尾を示すタプル

順序とデータのタプル

Page 79: drb09

:tail 0

initialize

Page 80: drb09

:tail 0

notify("Hello, World.")

Page 81: drb09

:tail 0

take([:tail, nil]) [:tail, 0]が取り除かれる

Page 82: drb09

1 "Hello, World."

write([tail, it])末尾に要素を追加

Page 83: drb09

:tail 1

1 "Hello, World."

write([:tail, tail])末尾のindexをメモ

Page 84: drb09

:tail 1

1 "Hello, World."

receive(1)

Page 85: drb09

:tail 1

1 "Hello, World."

read([1, nil])[1, "Hello, World.]のコピーが返る

Page 86: drb09

:tail 1

1 "Hello, World."

receive(2)

Page 87: drb09

:tail 1

1 "Hello, World."

read([2, nil])[2, nil]にマッチするタプルがないのでブロックする

Page 88: drb09

:tail 1

1 "Hello, World."

notify("Hello, Again.")

Page 89: drb09

:tail 1

1 "Hello, World."

take([:tail, nil]) [:tail, 1]が取り除かれる

Page 90: drb09

1 "Hello, World."

2 "Hello, Again."

write([tail, it])

Page 91: drb09

:tail 2

1 "Hello, World."

2 "Hello, Again."

write([:tail, tail])

Page 92: drb09

:tail 1

1 "Hello, World."

2 "Hello, Again."

read([2, nil])[2, nil]にマッチしたので再開しこのタプルが返る

Page 93: drb09

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

添字を増やしながら受信して印刷するループ

Page 94: drb09

演習4

古いデータを削除するなら、どんな仕組みを用意すればよいと思いますか?

Page 95: drb09

重要なことをもう一度

もう一度

Page 96: drb09

重要

まだ初刷買えます!

dRubyによる

関 将俊 著

分散・Webプログラミング

Page 97: drb09

まとめ

まだ初刷買えます。

dRubyの雰囲気を味わえた?

Page 98: drb09

ふりかえり