drb09

Post on 19-Jan-2015

615 views 0 download

Tags:

description

Slide of lecture on Shimane University on December 4

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の雰囲気を味わえた?

ふりかえり