国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

為RMI實現類Jini的發現機制

2019-11-18 12:41:51
字體:
來源:轉載
供稿:網友

  假如你從事過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多播數據包
                    開始使用服務。
  
  
  發現協議
  前面我們已經大致勾勒了客戶端怎樣發現服務器:它會指定一個接口類和一個唯一名字來確認一個服務器實例。這是因為多個實現相同接口的服務器可以同時運行。
  在實現我們的RMI發現機制之前,我們必須為在參與者之間傳遞的消息定義一個協議。簡單起見,我們用含定界符的字符串來包含RMI服務器對匹配的請求作出響應所需的全部信息。首先,我們定義一個協議頭。這防止了服務器類嘗試解析其他來源的數據包。消息數據包的剩余部分將包含一個單播響應端口,服務器的接口類名字,和服務器實例的唯一標識符。
  下面是我們將使用的發現請求消息的格式:
  <protocol>,<unicast port>,<interface class>,<unique id>
  現在我們看看一個消息數據包的例子,這個數據包是客戶端發送來發現Translator服務器的Spanish實例的。RMI-DISCOVERY是協議頭。5000是客戶端將監聽響應的端口號:
  RMI-DISCOVERY,5000,Translator,Spanish
  我們沒有在請求中包括客戶機的名字,因為這個信息可以從服務器收到的UDP包中獲得。定義了我們的消息格式,現在我們可以開始實現發現類了。
  
  實現服務器端的類
  我們的美好計劃是寫一個工具類,好讓RMI服務器用它來實現它們自己的查找服務:
  //初始化RMI服務器
  Remote server=new SpanishTranslator();
  
  //初始化發現監聽器
  RMILookup.bind(server,"Spanish")
  Remote參數用于檢查服務器是否是實現了客戶端所要訪問的接口,和哪一個RMI stub將最終被串行化返回給客戶端。String參數用于比較服務器的名字和請求包中指定的名字。
  在繼續之前,我們扼要重述一下服務器端的類的職責:
  1. 建立一個多播UDP socket以監聽請求
  2. 當數據包到達時檢查協議頭
  3. 解析消息數據包
  4. 匹配唯一服務器名字參數
  5. 匹配接口參數
  6. 假如步驟4和5匹配,將服務器的遠程代理(remote stub)通過單播TCP/IP socket串行化到客戶端
  
  建立多播UDP監聽器
  要建立一個多播監聽器,你必須使用一個確定的多播地址和端口;它的范圍在224.0.0.1到239.255.255.255之間(包括224.0.0.1和239.255.255.255)。有些廠商保留了一些地址/端口的聯合;例如,Sun為Jini保留了聯合224.0.1.85:4160。(被保留地址的列表可以在http://www.iana.org/assignments/multicast-addresses找到。)不推薦在和別的廠商相同的地址/端口聯合上運行,所以我們選擇了和MulticastSocket Javadoc(見文后Resources)例子相同的聯合:
  int port=6789;
  String multicastAddress="228.5.6.7";
  
  MulticastSocket socket=new MulticastSocket(port);
  InetAddress address=InetAddress.getByName(multicastAddress);
  socket.joinGroup(address);
  
  byte[] buf = new byte[512];
  DatagramPacket packet=new DatagramPacket(buf, buf.length);
  socket.receive(packet);
  //parse packet etc
  socket.leaveGroup(address);
  從上面的例子可以看出,你要建立一個多播監聽器并在此地址/端口聯合上接收數據包有多么簡單。在上面的例子中,只能處理單個數據包,所以我們必須在創建DatagramPacket和socket.receive()處建立循環;否則只有一個客戶端能夠發現這個服務器。
  while(active){
  byte[] buf=new byte[512];
  DatagramPacket packet=new DatagramPacket(buf,buf.length);
  socket.receive(packet);
  //PRocess packet
  }
  我們可以用一些策略來處理收到的數據包:
  1. 每請求線程:為每個請求創建一個新的線程來處理
  2. 來自線程池的線程:使用來自(可能固定的)資源線程池的預初始化的一個線程(見 "Java Tip 78: Recycle Broken Objects in Resource Pools,http://www.javaworld.com/javaworld/javatips/jw-javatip78.html";)
  3. 阻塞:在同一時間只處理一個請求,其他請求必須等待
  由于我們從客戶端發起一次發現,自然地,阻塞策略在這里是可行的。這是

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 弥勒县| 福鼎市| 山西省| 屏东县| 通江县| 镇平县| 清水县| 海兴县| 乡城县| 泽州县| 刚察县| 黄浦区| 百色市| 潞西市| 田林县| 平潭县| 东丽区| 象州县| 环江| 青海省| 平阳县| 时尚| 南汇区| 刚察县| 台中县| 沽源县| 邯郸县| 兰州市| 嘉黎县| 大悟县| 大英县| 洛隆县| 砚山县| 封丘县| 平潭县| 贵州省| 福建省| 凌海市| 南皮县| 龙胜| 绥江县|