最後,我們來看看Ruby中的分布式編程。現在網路已經非常普遍,我們有時候想在網路上傳遞各種對象,但是不幸的是,像CORBA,RMI這些協議使用起來非常費力,需要特殊規定的編碼,異常處理,而且還要在任何調用前定義介面。
Ruby對此有一個簡單的解決方案,消除了上面方法的繁瑣之處。分布式Ruby(也叫drb或者druby)是一個獨立的庫,完全由Ruby寫成,通過這個庫,你可以通過TCP在不同的Ruby進程中傳送各種對象(Ruby對象),而且只需要很少的步驟。
清單8顯示了這樣的一個例子,這個服務端共用了一個對象,通過這個對象,你可以得到伺服器的時間。第3行到第7行定義了這個取得本地時間要被共用的對象,第9行將這個對象綁定到一個drb伺服器(本例中連接埠為2222),因為伺服器程式在一個獨立的線程,所以第10行確保主程式會在這個線程結束後才能退出。
清單8: A simple distributed Ruby server 1 require 'drb' 2 3 class Info 4 def get_time 5 "It is now #{Time.now}" 6 end 7 end 8 9 DRb.start_service("druby://your.host.name:2222", Info.new)10 DRb.thread.join |
清單9是一個用戶端程式,也非常簡單,第4行的DRbObject 調用和遠程伺服器建立了一個串連,返回了遠程對象的一個代理,然後,你就可以像調用本機物件一樣調用遠程對象的方法了。
清單9: A simple distributed Ruby client 1 require 'drb' 2 3 DRb.start_service 4 info = DRbObject.new(nil, "druby://your.host.name:2222") 5 6 3.times do 7 puts info.get_time 8 sleep 2 9 end |
下面我們來看一下另一個很有意思的東西tuplesapces,它是David Gelernter在Linda系統中首先提出來的,tuplespaces像一個共用的bbs系統,程式可以向它貼訊息,或者從它取得訊息,這裡所說的訊息都是一些值組成的數組。out方法用來向tuplespace寫訊息,所以,下面的代碼代碼建立了一個含有4條內容的tuplespace:
require 'tuplespace'ts = TupleSpace.new ts.out [ "dave", "car", "blazer" ]ts.out [ "dave", "computer", "dell" ]ts.out [ "andy", "car", "explorer" ]ts.out [ "andy", "os", "linux" ]
使tuplespaces有趣的是你要想取得它裡面的內容,你不是根據地址來取,而是根據內容本身(通過匹配)來取。Ruby實現的tuplespace更加強大,取得訊息的匹配模式可以是值,對象的類,Regex,range等等。nil表示你不關心它的內容是什麼,即表示任何東西都匹配nil。要想取得訊息內容可以用in方法,如果in方法中指定的模式能在tuplespace中找到匹配的項,那麼這個匹配的項將被從tuplespace中刪除,並且將這項返回給調用者;否則的話,in將等待直到有匹配的項目出現。如果有多個記錄匹配in中指定的模式,那麼將會隨機的返回其中的一個記錄。
繼續前面的例子,下面的例子從tuplespace中讀取已經存入的記錄,注意最後一個語句,它使用了Regex作為匹配的模式。
# read one of Dave's possessionsres1 = ts.in ["dave", nil, nil]# someone owning a carres2 = ts.in [nil, "car", nil]# a possession containing the "x" or "z"res3 = ts.in [nil, nil, /[xz]/]
從這個簡單的例子開始,你能輕鬆地編寫出複雜,協作,並行的系統。
比如,你可以用tuplespace解決複雜地AI問題。一個進程可以通過將一組資料群組合起來放到tuplespace中來產生一個問題,其他進程則通過匹配地方式從tuplespace中讀取它能解決地問題;一個進程得到一個錯誤地時候,它也可能把這個問題細分為更小的問題 ,然後將它們放到tuplespace裡面。其它的進程又取得這個問題,然後解決它們或者繼續分解這些問題。
這個過程將繼續,直到所有的問題都得到解決。
為了完成這篇文章,我們將使用drb和tuplespace編寫一個簡單的P2P的聊天程式,這個系統將由三個元素組成的訊息存放在tuplespace中,即訊息的寄件者,接收者,和訊息內容。
用戶端的程式在清單10中,用戶端由兩個線程一起運行,發送線程是主線程,在它裡面顯示的建立了接收線程。發送線程從第16行到22行,它從使用者的控制台讀取字串,格式如下:
to: message text
to是接收者姓名,第17行將上面的輸入分成兩部分,前面的是姓名,剩下的是訊息內容。然後,第19行將這個訊息組成一個數組寫到tuplespace裡面。
清單10: A chat client based on tuplespaces 1 require 'drb' 2 require 'tuplespace' 3 4 DRb.start_service 5 ts = DRbObject.new(nil, / "druby://server.host:12321") 6 7 my_name = ARGV[0] 8 9 Thread.new do10 loop do11 from, unused, line = ts.in / [ nil, my_name, nil ]12 puts "#{from} says: #{line}"13 end14 end1516 while line = gets17 to, text = line.split(/:/, 2)18 if text19 ts.out [ my_name, to, text ]20 else21 puts '** use "to: message"'22 end23 end |
接收線程也很簡單,第9行到14行運行一個簡單的迴圈,從tuplespace中讀取資料,匹配條件是訊息接收者是我們自己的名字,然後將結果列印在控制台上。
Tuplespaces需要一個伺服器存放tuples,在Ruby中用drb伺服器來存放tuplespace對象,可以用下面代碼實現:
require 'drb'require 'tuplespace'DRb.start_service("druby://server.host:12321", / TupleSpace.new)DRb.thread.join
首先運行服務端程式,然後運行用戶端,並且在用戶端程式指定你想用的名字作為參數。
如果你運行它你會發現,如果你不線上,伺服器端會把給你的訊息儲存在伺服器上。我們的這個應用程式不恩那個取代Yahoo IM,Jabber或者IRC等,但這段代碼卻非常有用。