JGroups(一)

1 Overview
    JGroups是一個用於建立可靠的組播通信的工具包(這裏指的組播並不一定是IP Multicast,JGroups同樣支持使用TCP作爲傳輸協議)。其中可靠性是指通過適當的配置可以保證:消息在傳輸的過程中不會丟失;所有的接收者以相同的順序接受所有的消息;原子性:一個消息要麼被所有的接收者接收,要麼不被任何一個接收者都接收。目前在JBoss Application Server Clustering,OSCache Clustering,Jetty HTTP session replication,  Tomcat HTTP session replication中都使用了JGroups。
Unreliable Reliable
Unicast UDP TCP
Multicast IP Multicast JGroups
    TCP和UDP是單播(Unicast)協議,也就是說:發送者和每一接收者之間是點對點傳輸。 如果一個發送者希望向多個接收者傳輸相同的數據,那麼必須相應的複製多份數據。TCP是可靠的傳輸協議,但UDP不是可靠的,也就是說報文在傳輸的過程中可能丟失、重複或着亂序,報文的最大尺寸也有限制。IP Multicast可以將消息同時發送到多個接收者。由於IP Multicast是基於UDP的,因此IP Multicast是不可靠的。IP Multicast需要一個特殊的組播地址,它是一組D類IP地址,範圍從224.0.0.0 到 239.255.255.255,其中有一部分地址是爲特殊的目的保留的。JGroups使用UDP (IP Multicast)、TCP、JMS作爲傳輸協議。JGroups最強大的功能之一是提供了靈活的,可定製的協議棧,以滿足不同的需求。例如,如果選擇使用IP Multicast作爲傳輸協議,那麼爲了防止報文丟失和重複,可以在協議棧中添加NAKACK協議;爲了保證報文的順序,可以在協議棧中添加TOTAL協議,以保證FIFO的順序;爲了在組內成員發生變化時得到通知和回調,可以添加Group Membership Service (GMS) 和 FLUSH協議;Failure Detector (FD)協議用於識別組內崩潰的成員;如果新加入的成員希望獲得組內其它成員維護的狀態,那麼可以向協議棧中添加STATE_TRANSFER協議;如果希望對傳輸的數據進行加密,那麼可以使用CRYPT協議等等。

    JGruops的主要功能有:
組的創建和刪除。組可以跨越LANs或者WANs。
加入組、主動或者被動(例如當機或者網絡故障)離開組。
在組成員改變時,組中其它成員可以得到通知。
向組中的單個或者多個成員發送消息。
    在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內的第一個實例被稱爲coordinator。Receiver接口上的getState(),setState()方法用於在實例間傳遞狀態。新的實例通過setState()方法獲得通過狀態,而這個狀態是通過調用集羣中其它某個實例上的getState()獲得的。
    以下是JGruops manual中的一個簡單的例子:

import java.io.BufferedReader;  
import java.io.InputStreamReader;  
import java.util.LinkedList;  
import java.util.List;  
  
import org.jgroups.JChannel;  
import org.jgroups.Message;  
import org.jgroups.ReceiverAdapter;  
import org.jgroups.View;  
import org.jgroups.util.Util;  
  
public class SimpleChat {  
    //  
    private JChannel channel;  
    private List<String> state = new LinkedList<String>();  
    private String userName = System.getProperty("user.name", "WhiteSock");  
      
    public void start() throws Exception {  
        //  
        channel = new JChannel();  
        channel.setReceiver(new ReceiverAdapter() {  
              
            public void receive(Message msg) {  
                System.out.println(msg.getSrc() + ": " + msg.getObject());  
                  
                synchronized(state) {  
                    state.add((String)msg.getObject());  
                }  
            }  
              
            public void viewAccepted(View view) {  
                System.out.println("view accepted: " + view);  
            }  
              
            public byte[] getState() {  
                synchronized(state) {  
                    try {  
                        return Util.objectToByteBuffer(state);  
                    }  
                    catch(Exception e) {  
                        e.printStackTrace();  
                        return null;  
                    }  
                }  
            }  
              
            @SuppressWarnings("unchecked")  
            public void setState(byte[] new_state) {  
                try {  
                    List<String> list=(List<String>)Util.objectFromByteBuffer(new_state);  
                    synchronized(state) {  
                        state.clear();  
                        state.addAll(list);  
                    }  
                    System.out.println("received state (" + list.size() + " messages in chat history):");  
                    for(String str: list) {  
                        System.out.println(str);  
                    }  
                }  
                catch(Exception e) {  
                    e.printStackTrace();  
                }  
            }  
        });  
        channel.connect("ChatCluster");  
        channel.getState(null, 10000);  
          
        //  
        sendMessage();  
          
        //  
        channel.close();  
    }  
      
    private void sendMessage() throws Exception {  
        boolean succeed = false;  
        BufferedReader br = null;  
        try {  
            br = new BufferedReader(new InputStreamReader(System.in));  
            while(true) {  
                System.out.print(">");  
                System.out.flush();  
                String line = br.readLine();  
                if(line != null && line.equals("exit")) {  
                    break;  
                } else {  
                    Message msg = new Message(null, null, "[" + userName + "]" + line);  
                    channel.send(msg);  
                }  
            }  
            succeed = true;  
        } finally {  
            if(br != null) {  
                try {  
                    br.close();  
                } catch (Exception e) {  
                    if(succeed) {  
                        throw e;  
                    }  
                }  
            }  
        }  
    }  
      
    public static void main(String args[]) throws Exception {  
        new SimpleChat().start();  
    }  
}  
 

在以上例子中,主線程會阻塞,直到從stdin中讀取一行。如果這行是"exit",那麼程序退出,否則向集羣中發送一個消息。如果集羣中某個實例強行退出,那麼集羣中的其它實例也會得到通知。Message構造函數的第一個參數如果是null,那麼意味着消息將被髮送到集羣內所有的實例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章