假如你從事過Jini開發,你會知道Jini客戶端是不需要知道服務的位置的;它們簡單地通過發現機制來獲得一個代理以訪問它們需要的服務。相反,在RMI(遠程方法調用)中,你必須知道你想訪問的服務器的URL。在本文中,我們將向你展示怎樣為RMI實現一個類Jini的發現機制,這將使得一些客戶端從必須知道RMI服務器URL的麻煩中解脫出來。
你可能首先會想,為什麼要這麼麻煩;為什麼不干脆用Jini?我們也同意這樣的邏輯,非凡是對新的系統來說。不管怎樣,已經有許多基於RMI的系統存在,並且在Jini被Java開發的主流接受以前,我們仍然要提供更優雅的RMI解決方案。事實上,我們在這兒描述的工作,是這樣的需求的結果:開發一項Jini服務使它同時可以作為一個獨立的RMI服務器運行,但使用類Jini的發現機制。
本文主要是針對沒有用過Jini的RMI開發者。通過深入觀察Jini內部的運作,我們希望你能開始了解Jini的機制有多麼強大。我們當然不是希望你重新實現Jini,但這篇文章能幫助你理解這些機制是怎樣運作的。甚至可能幫助你說服你的經理或部門頭頭,該考慮將Jini作為一項可行的分布式系統技術。
我們不會太深入Jini的發現機制,所以假如你對此不是很熟悉,我們建議你快速浏覽一下Bill Venners的"Locate Services with the Jini Lookup Service."( http://www.javaworld.com/javaworld/jw-02-2000/jw-02-jiniology.Html)
RMI基礎和Jini查找
在RMI中,客戶端必須知道它所要連接的服務器的位置。RMI服務器的地址是URI的形式rmi://<主機>:<端口>/<服務名>,其中端口號是rmiregistry用來偵聽請求的端口。例如:
Translator service
=(Translator)Naming.lookup("rmi://theHost/SpanishTranslator");
在Jini中,客戶端通過一個Jini工具類來找到服務,比如ServiceDiscoveryManager。在下面的例子中,我們創建了一個ServiceTemplate的實例,該實例包含一個類列表;在我們的例子中,是我們要匹配的類??Translator.class:
Class [] classes=new Class[]{Translator.class};
ServiceTemplate tmpl=new ServiceTemplate(null,classes,null);
ServiceDiscoveryManager lmgr=new ServiceDiscoveryManager(null,null);
ServiceItem serviceItem =lmgr.lookup(tmpl,null);
Translator service=serviceItem.service;
正如我們從例子中可以看到,ServiceDiscoveryManager用lookup()方法來查找任何與ServiceTemplate匹配的可用的服務。你還可以在服務查找中使用任何數字或屬性;在這裡我們出於保持簡單和精練的考慮而沒有這樣做。
比較兩種查找機制,你會注重到在Jini版本中沒有指定服務的位置。值得一提的是,假如必要,你也可以指定一個查找服務的位置,但不是你想要訪問的實際服務的位置。Jini模型的強大之處是,我們不需要知道或關心服務位於何處。
比較了RMI和Jini的發現機制之後,現在我們可以考慮怎樣用類Jini的風格來訪問一個RMI服務器。
位置中立的RMI查找
理想地,我們考慮查找Translator所發現的第一個匹配的實例。
Translator service
=(Translator)RMIDiscovery.lookup(clazz,id);
在這裡clazz是RMI服務的接口,id是區分實現clazz接口的不同服務器實例的唯一字符串標識。例如,要找到一個西班牙語翻譯器,我們用下面的代碼:
Class clazz=Translator.class;
String id="Spanish";
現在我們對如何使用RMI發現機制有了一個更好的主意,我們來研究一下怎樣實現它。在我們嘗試實現我們“簡陋的”RMI發現機制以前,先來看看Jini是怎樣做的,再把這些原理/概念適用到RMI服務器和客戶端上。
發現機制
Jini的基本發現機制聯合使用多播UDP(用戶數據報協議)(multicast UDP 見文後的Resources)和單播TCP/IP。簡單來說,這意味著客戶端發出一個多播的請求數據包,然後數據包被監聽它的查找服務拾取。然後查找服務用單播連接連回客戶端,並把查找服務的代理串行化成流通過此連接發送出去。此後客戶端就可以和查找服務(的代理)交互以定位它需要的服務。
發現機制實際上比這要復雜得多,但我們只對其中多播UDP和單播TCP/IP的要害概念感愛好。我們並不打算實現一個等同的獨立運行的RMI查找服務。相反我們將實現一個簡單的多播監聽器/單播分發器(multicast listener/unicast dispatcher)供RMI服務器使用,實際上我們使得每個RMI服務器作為它自己的查找服務。在客戶端,我們為服務器端socket寫個配對物??一個多播分發器/單播監聽器(multicast dispatcher/unicast listener)。
下面的表更具體地說明了RMI客戶端和RMI服務器端間的交互。
RMI客戶端和RMI服務器端的交互
服務器端客戶端
在多播地址上開始監聽
建立ServerSocket以監聽來自服務器的單播響應。
開始向多播地址發送UDP數據包
解析收到的UDP數據包。假如有效,通
過單播TCP/IP連回客戶端。
向客戶端發送遠程代理(remote stub)。
從流中讀取遠程對象。
關閉ServerSocket。停止發送UDP多播數據包
開始使用服務。
發現協議
前面我們已經大致勾勒了客戶端怎樣發現服務器:它會指定一個接口類和一個唯一名字來確認一個服務器實例。這是因為多個實現相同接口的服務器可以同時運行。