JGroups簡介和例子

JGroups是一個組播通信工具,它可以:
[list]
[*]創建和刪除一個組
[*]加入和離開某個組
[*]管理組成員關係,當有新的成員進入或存在的成員離開的時候會通知組內其它成員
[*]偵測和移除出現故障的組成員
[*]發送單播消息(unicast,point-to-point)
[*]發送廣播消息(multicast,point-to-multipoint)
[/list]
JGroups的強大之處在於它有一個很靈活的協議棧,可以根據你的需要,隨意的添加或刪除某些功能。比如說,剛開始你使用IP廣播發送你的消息,過了一會程序開始要求無損的消息傳輸,你可以添加NAKACK協議,它能確保接收方一定能收到你發送的消息。但是此時接收方收到的消息的順序是不固定的,爲了讓接收順序和發送順序保持一致,你可以選擇添加FIFO協議來確保一對收發者之間發送和接收的順序。如果要確保組裏所有成員的收發順序,你可以添加TOTAL協議。再接下來,你可以添加GMS和FLUSH協議來維護組成員間的關係;FD協議可以進行故障檢測;STATE_TRANSFER協議可以讓新加入的組成員從已存在的成員中獲取一致的狀態;最後你還可以使用CRYPT協議來加密你發送的消息。

下面開始演示一個聊天組的程序。我們建立一個聊天組,分別發送單播消息和廣播消息,當組成員發生變化的時候,所有組成員自動獲得新的組成員視圖,每當聊天組中加入一個新的成員的時候,新成員先和已存在的組成員進行狀態同步(獲取聊天記錄)。

public class GroupChat extends ExtendedReceiverAdapter{

private JChannel channel;
private List<String> msgList = new ArrayList<String>(); //模擬狀態對象,保存的是本節點的收到的消息

/**
* 在有節點向本節點請求狀態的時候被調用
*/
@Override
public byte[] getState() {
byte[] state = null;
synchronized(msgList){
try {
state = Util.objectToByteBuffer(msgList);
} catch (Exception e) {
e.printStackTrace();
}
return state;
}
}

/**
* 在其他節點返回狀態給本節點的時候被調用
*/
@Override
public void setState(byte[] state) {
synchronized(msgList){
try {
List<String> tmpList = (List<String>)Util.objectFromByteBuffer(state);
msgList.clear();
msgList.addAll(tmpList);
System.err.println("===receive state:[");
for(String msg : msgList){
System.out.println(msg);
}
System.out.println("]");
} catch (Exception e) {
e.printStackTrace();
}
}
}

/**
* 當有消息進來的時候被調用
*/
@Override
public void receive(Message msg) {
System.out.println(">>>new message receive from "+msg.getSrc()+":"+msg.getObject());
msgList.add(msg.getSrc()+" send : "+ msg.getObject());
}

/**
* 當組成員發生變化的時候被調用
*/
@Override
public void viewAccepted(View new_view) {
System.out.println("***new view receiver:"+new_view);
}

public void start() throws ChannelException{
//打開channel並指定配置文件
channel = new JChannel("udp.xml");
//指定組名,就可以創建或連接到廣播組
channel.connect("ChatGroup");
//註冊回調接口,使用"推"模式來接受廣播信息
channel.setReceiver(this);
//查看當前組成員
System.out.println("---current view:"+channel.getView());
//狀態同步,第一個參數爲null則向協調者節點獲取信息
channel.getState(null, 1000);
}

public void close(){
channel.close();
}

public void loopSendMessage(){
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
while(true){
System.out.println("please input a string");
String line = br.readLine();
System.out.println("you put:"+line);
if(line.equals("exit")){
break;
}else{
String[] array = line.split(",");

Address des = null; //接收方地址,爲null代表發送廣播消息
Address src = null; //發送方地址,爲null代表自己的地址
String msg = line; //發送內容

if(array.length == 3){
des = new IpAddress(Integer.parseInt(array[0]));
src = new IpAddress(Integer.parseInt(array[1]));
msg = array[2];
}else if(array.length == 2){
des = new IpAddress(Integer.parseInt(array[0]));
msg = array[1];
}

Message message = new Message(des, src, msg);
//發送消息
channel.send(message);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ChannelNotConnectedException e) {
e.printStackTrace();
} catch (ChannelClosedException e) {
e.printStackTrace();
}
}


public static void main(String[] args) {
GroupChat chat = new GroupChat();
try {
chat.start();
chat.loopSendMessage();
chat.close();
} catch (ChannelException e) {
e.printStackTrace();
}

}

}

可以看到JGroups提供的API屏蔽了底層的通信機制,對於開發人員來說是完全透明的,要關注的只是消息的接受處理就可以了。
對應的協議棧配置:

<config>
<UDP
mcast_group_addr="${jgroups.udp.mcast_addr:228.10.10.10}"
mcast_port="${jgroups.udp.mcast_port:45588}"
tos="8"
ucast_recv_buf_size="20000000"
ucast_send_buf_size="640000"
mcast_recv_buf_size="25000000"
mcast_send_buf_size="640000"
loopback="false"
discard_incompatible_packets="true"
max_bundle_size="64000"
max_bundle_timeout="30"
use_incoming_packet_handler="true"
ip_ttl="${jgroups.udp.ip_ttl:2}"
enable_bundling="true"
enable_diagnostics="true"
thread_naming_pattern="cl"

use_concurrent_stack="true"

thread_pool.enabled="true"
thread_pool.min_threads="1"
thread_pool.max_threads="25"
thread_pool.keep_alive_time="5000"
thread_pool.queue_enabled="false"
thread_pool.queue_max_size="100"
thread_pool.rejection_policy="Run"

oob_thread_pool.enabled="true"
oob_thread_pool.min_threads="1"
oob_thread_pool.max_threads="8"
oob_thread_pool.keep_alive_time="5000"
oob_thread_pool.queue_enabled="false"
oob_thread_pool.queue_max_size="100"
oob_thread_pool.rejection_policy="Run"/>

<PING timeout="2000"
num_initial_members="3"/>
<MERGE2 max_interval="30000"
min_interval="10000"/>
<FD_SOCK/>
<FD timeout="10000" max_tries="5" shun="true"/>
<VERIFY_SUSPECT timeout="1500" />
<BARRIER />
<pbcast.NAKACK max_xmit_size="60000"
use_mcast_xmit="false" gc_lag="0"
retransmit_timeout="300,600,1200,2400,4800"
discard_delivered_msgs="true"/>
<UNICAST timeout="300,600,1200,2400,3600"/>
<pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
max_bytes="400000"/>
<VIEW_SYNC avg_send_interval="60000" />
<pbcast.GMS print_local_addr="true" join_timeout="3000"
join_retry_timeout="2000" shun="false"
view_bundling="true"/>
<FC max_credits="20000000"
min_threshold="0.10"/>
<FRAG2 frag_size="60000" />
<!--pbcast.STREAMING_STATE_TRANSFER /-->
<pbcast.STATE_TRANSFER />
<!-- pbcast.FLUSH /-->
</config>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章