Jgroups初步講解

轉自:http://xpenxpen.iteye.com/blog/2041897

1.官方的tutorial已經很好了,得首先閱讀。 

http://www.jgroups.org/ug.html 

本文其實是官方的tutorial的筆記,其中大部分文字轉載自http://whitesock.iteye.com/blog/199229 

2.基本概況 
在JGroups中JChannel類提供了主要的API ,用於連接到集羣(cluster)、發送和接收消息(Message)和註冊listeners等。 
Message包含消息頭(保存地址等信息)和一個字節數組(保存希望傳輸的數據)。org.jgroups.Address接口及其實現類封裝了地址信息,它通常包含IP地址和端口號。 
連接到集羣中的所有實例(instance)被稱爲一個視圖(org.jgroups.View)。通過View.getMembers()可以得到所有實例的地址。 
實例只有在連接到集羣后才能夠發送和接收消息。 
以相同name調用JChannel.connect(String name)方法的所有實例會連接到同一個集羣。 
當實例希望離開集羣時,可以調用JChannel.disconnect()方法。當希望釋放佔有的資源時,可以調用JChannel.close()方法。JChannel.close()方法內部會調用JChannel.disconnect()方法。 

通過調用JChannel.setReceiver()方法可以接收消息和得到View改變的通知。每當有實例加入或者離開集羣的時候,viewAccepted(View view)方法會被調用。 
View.toString()方法會打印出View中所有實例的地址,以及View ID。 
需要注意的是,每次viewAccepted(View view)方法被調用時,view參數都不同,其View ID也會增長。 
如果沒有名字,名字是機器名+隨機數,後面跟|,以及自增長的View ID。 
View內的第一個實例被稱爲coordinator。 
Receiver接口上的getState(),setState()方法用於在實例間傳遞狀態。 
新的實例通過setState()方法獲得通過狀態,而這個狀態是通過調用集羣中其它某個實例上的getState()獲得的。 

3.Chat例子 
3.1 實現功能 
我們來寫一個聊天程序,只支持文本的。我們要實現如下功能 
  • 所有的SimpleChat實例可以相互找到並組成一個集羣。
  • 沒必要創建一箇中心的ChatServer,這樣就不會有單點故障。
  • 聊天消息將被髮到集羣中的所有實例。
  • 當一個實例加入或退出(或崩潰)時,其他實例都將得到通知。
  • 我們維護一個集羣內的聊天記錄state。新加入的實例可以查詢聊天記錄。


3.2 代碼 
代碼就是官方的例子,我加入了詳細的註釋。 
Java代碼  收藏代碼
  1. import java.io.BufferedReader;  
  2. import java.io.DataInputStream;  
  3. import java.io.DataOutputStream;  
  4. import java.io.InputStream;  
  5. import java.io.InputStreamReader;  
  6. import java.io.OutputStream;  
  7. import java.util.LinkedList;  
  8. import java.util.List;  
  9.   
  10. import org.jgroups.JChannel;  
  11. import org.jgroups.Message;  
  12. import org.jgroups.ReceiverAdapter;  
  13. import org.jgroups.View;  
  14. import org.jgroups.util.Util;  
  15.   
  16. public class SimpleChat extends ReceiverAdapter {  
  17.     JChannel channel;  
  18.     String user_name = "ABC";  
  19.     private List<String> state = new LinkedList<String>();  
  20.   
  21.     private void start() throws Exception {  
  22.         channel = new JChannel(); //使用默認配置udp.xml  
  23.         channel.setReceiver(this); //指定Receiver用來收消息和得到View改變的通知  
  24.         channel.connect("ChatCluster"); //連接到集羣  
  25.           
  26.         //剛加入集羣時,我們通過getState()獲取聊天曆史記錄  
  27.         //getState()的第一個參數代表目的地地址,這裏傳null代表第一個實例(coordinator)  
  28.         //第二個參數代表等待超時時間,我們等待10秒。如果時間到了,State傳遞不過來,會拋例外。也可以傳0代表永遠等下去  
  29.         channel.getState(null10000);  
  30.         eventLoop();  
  31.         channel.close();  
  32.     }  
  33.   
  34.     private void eventLoop() {  
  35.         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));  
  36.   
  37.         while (true) {  
  38.             try {  
  39.                 System.out.print("> ");  
  40.                 System.out.flush();  
  41.                 String line = in.readLine().toLowerCase();  
  42.                 if (line.startsWith("quit") || line.startsWith("exit")) {  
  43.                     break;  
  44.                 }  
  45.                 line = "[" + user_name + "] " + line;  
  46.   
  47.                 //Message構造函數的第一個參數代表目的地地址,這裏傳null代表要發消息給集羣內的所有地址  
  48.                 //第二個參數表示源地址,傳null即可,框架會自動賦值  
  49.                 //第三個參數line會被序列化成byte[]然後發送,推薦自己序列化而不是用java自帶的序列化  
  50.                 Message msg = new Message(nullnull, line);  
  51.                 channel.send(msg); //發消息到集羣  
  52.   
  53.             } catch (Exception e) {  
  54.             }  
  55.         }  
  56.     }  
  57.       
  58.     @Override  
  59.     //每當有實例加入或者離開集羣(或崩潰)的時候,viewAccepted方法會被調用  
  60.     public void viewAccepted(View new_view) {  
  61.          System.out.println("** view: " + new_view);   
  62.     }  
  63.   
  64.     @Override  
  65.     //有消息時,byte[]會被反序列化成Message對象,也可以用Message.getBuffer得到byte[]然後自己反序列化。  
  66.     public void receive(Message msg) {  
  67.         String line = msg.getSrc() + ": " + msg.getObject();  
  68.         System.out.println(line);  
  69.         //加入到歷史記錄  
  70.         synchronized (state) {  
  71.             state.add(line);  
  72.         }  
  73.     }  
  74.       
  75.     @Override  
  76.     public void getState(OutputStream output) throws Exception {  
  77.         //當JChannel.getState()被調用時,某個原來就在集羣中的實例的getState會被調用用來得到集羣的共享state  
  78.         //Util.objectToStream方法將state序列化爲output二進制流   
  79.         synchronized (state) {  
  80.             Util.objectToStream(state, new DataOutputStream(output));   
  81.         }  
  82.     }  
  83.       
  84.     @Override  
  85.     public void setState(InputStream input) throws Exception {  
  86.         //當以上集羣的共享state被得到後,新加入集羣的實例的setState方法就會被調用了  
  87.         List<String> list = (List<String>) Util.objectFromStream(new DataInputStream(input));  
  88.         synchronized (state) {  
  89.             state.clear();  
  90.             state.addAll(list);  
  91.         }  
  92.   
  93.         System.out.println(list.size() + " messages in chat history):");  
  94.         for (String str : list) {  
  95.             System.out.println(str);  
  96.         }  
  97.     }  
  98.       
  99.     public static void main(String[] args) throws Exception {  
  100.         new SimpleChat().start();  
  101.     }  
  102.   
  103. }  


3.3 功能測試 

1.運行該代碼3次,開啓了3個實例,觀察控制檯,可以看到每有一個實例加入集羣,其他客戶端都會得到通知(viewAccepted被調用)。 
2.隨便哪個客戶端發一條消息,其他客戶端都能收到這條消息。 
3.其中一個客戶端輸入exit,其他客戶端都會得到通知。 
4.模擬崩潰,可以殺死某個客戶端進程,可以觀察到其他客戶端可以得到通知。 
5.新加入的客戶端可以看到聊天曆史記錄。 

3.4 監控測試 

爲了探索jgroups的內在機理,我們用Process Explorer做另一個測試。 
先開啓第1個SimpleChat 
看到第1臺機器的53242開始監聽 
 

開啓第2個SimpleChat 
第1臺機器的53244和第2臺機器的53243建立連接 
第2臺機器的53245和第1臺機器的53242建立連接 


開啓第3個SimpleChat 
第1臺機器的53244和第2臺機器的53243建立連接 
第2臺機器的53247和第3臺機器的53246建立連接 
第3臺機器的53248和第1臺機器的53242建立連接 


開啓第4個SimpleChat 
第1臺機器的53244和第2臺機器的53243建立連接 
第2臺機器的53247和第3臺機器的53246建立連接 
第3臺機器的53250和第4臺機器的53249建立連接 
第4臺機器的53251和第1臺機器的53242建立連接 


殺死第3個SimpleChat 
第1臺機器的53244和第2臺機器的53243建立連接 
第2臺機器的53252和第4臺機器的53249建立連接 
第4臺機器的53251和第1臺機器的53242建立連接 


我們管中窺豹,略微嗅到了jgroups是怎樣實現可靠多播的,就是採用一個環將各個節點連接起來(TCP連接)。 
 

當有一個節點崩潰(Client3),這個環會重新連接成一個新的環。圖中的藍線便是爲了修補這個環所建立的新的連接。 
 

圖中紅色的端口是UDP的意思,這個端口負責多播通訊,圖中看可出是45588端口,jgroups.jar包默認的udp.xml印證了這一點。 
Java代碼  收藏代碼
  1. <UDP  
  2.      mcast_port="${jgroups.udp.mcast_port:45588}" />  


4.jgroups的應用 
上面例子程序我們已經可以看到,jgroups可以用來做state replication 
以下項目場景都使用了jgroups 
JBoss Application Server Clustering 
OSCache Clustering 
Jetty HTTP session replication 
Tomcat HTTP session replication 

5.參考資料 
官方文檔部分中文翻譯 
https://community.jboss.org/wiki/BelaBansJGroupsManualTranslationSerialI-  共4篇 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章