最近研究 JAVA 集羣技術,看到 jgroups 這個框架,網上有些例子,非常簡單。
可以參考其官方網址:http://www.jgroups.org/manual/index.html
按捺不住,自己還是動手寫了一個試試。
代碼如下:
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;
import org.jgroups.stack.GossipRouter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* JGroups 測試
*
* @author hjj2017
* @since 2016/1/14
*
*/
public class HelloJGroups extends ReceiverAdapter { // <-- 注意這裏, HelloJGroups 即是消息的發送者又是消息的接收者
/** 用戶名稱 */
private String _userName = null;
/** JChannel */
private JChannel _channel = null;
/**
* 開始測試
*
* @throws Exception
*
*/
private void start() throws Exception {
// 在這裏生成用戶名, User.00
this._userName = "User." + (int)(Math.random() * 100);
// 創建 JChannel
this._channel = new JChannel();
this._channel.setReceiver(this);
this._channel.connect("ChatCluster");
// 事件循環
this.eventLoop();
// 事件循環結束之後,
// 關閉 JChannel
this._channel.close();
}
/**
* 事件循環, 從終端讀取文字
*
*/
private void eventLoop() {
// 創建讀入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
try {
// 輸出提示符
System.out.print("~> ");
System.out.flush();
// 從終端讀取文字
String ln = br.readLine();
if (ln.equalsIgnoreCase("quit") ||
ln.equalsIgnoreCase("exit")) {
// 遇到 quit / exit 時,
// 退出當前循環
break;
}
// 創建併發送消息,
// 消息內容是 "${userName} : ${ln}"
Message msg = new Message(null, null, this._userName + " : " + ln);
this._channel.send(msg);
} catch (Exception ex) {
// 輸出錯誤日誌
ex.printStackTrace();
}
}
}
@Override
public void viewAccepted(View v) {
System.out.println("viewAccepted : " + v);
}
@Override
public void receive(Message msg) {
System.out.println(msg.getObject());
}
/**
* 應用程序主函數
*
* @param args
* @throws Exception
*
*/
public static void main(String[] args) throws Exception {
new HelloJGroups().start();
}
}
這事一個簡單的聊天程序,可以啓動兩次來觀察結果。
-------------------------------------------------------------------
GMS: address=WINX-HOME-50829, cluster=ChatCluster, physical address=192.168.1.2:52477
-------------------------------------------------------------------
viewAccepted : [WINX-HOME-50829|0] [WINX-HOME-50829]
~>
-------------------------------------------------------------------
GMS: address=WINX-HOME-1927, cluster=ChatCluster, physical address=192.168.1.2:59569
-------------------------------------------------------------------
viewAccepted : [WINX-HOME-50829|1] [WINX-HOME-50829, WINX-HOME-1927]
~>
可以看到,第二個啓動的“WINX-HOME-1927”發現了第一個啓動的“WINX-HOME-50829”。
注意我是在本地測試的,這個程序啓動時會臨時綁定一個端口。
啓動兩次,綁定兩個不同的端口,會話過程是在同一臺機器上的兩個不同端口之間進行的。
程序啓動之後,這兩個程序會互相發現對方,這是這個框架一個比較方便的地方。
如果是在同一局域網裏的兩臺不同的機器上會是什麼結果呢?
我在家裏的兩臺 PC 機上測試過,兩臺 PC 的 IP 地址不相同(192.168.1.2 和 192.168.1.6),
啓動後仍然可以發現對方!
當然,這裏面有個前提,兩臺機器連接着同一臺路由器。
在真實的服務器環境中,所有的服務器都連接同一臺路由器是不可能的!
爲此,我們可以啓動 GossipRouter,令所有的 JGroups 程序都連接到這個 GossipRouter 上。
我們大概需要做以下 3 步:
-
第 1 步,啓動 GossipRouter,綁定 12001 端口:
java -Djava.net.preferIPv4Stack=true -cp .:commons-logging-1.1.3.jar:log4j-1.2.17.jar:jgroups-2.9.0.GA.jar org.jgroups.stack.GossipRouter -port 12001
-
第 2 步,創建 myConf.xml 文件,文件內容大致如下:
<?xml version="1.0" encoding="utf-8" ?>
<config xmlns="urn:org:jgroups"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd">
<TCP />
<!--// 注意 : 下面的 xxx.xxx.xxx.xxx 需要換成真實 IP //-->
<TCPGOSSIP
timeout="3000"
initial_hosts="xxx.xxx.xxx.xxx[12001]"
num_initial_members="3"
/>
<!--// 我試驗過 TCPPING, 但是失敗了 //-->
<VERIFY_SUSPECT timeout="1500" />
<pbcast.NAKACK
use_mcast_xmit="false"
retransmit_timeout="300,600,1200,2400,4800"
discard_delivered_msgs="true"
/>
<pbcast.STABLE
stability_delay="1000"
desired_avg_gossip="50000"
max_bytes="400000"
/>
<pbcast.GMS
print_local_addr="true"
join_timeout="5000"
view_bundling="true"
/>
</config>
- 第 3 步,修改 JChannel 創建代碼,這是最後一步:
// 只能使用文件絕對路徑,
// 使用相對路徑, JChannel 會產生歧義
final String xmlAbsPath = ClassLoader.getSystemResource(".").getPath() + "myConf.xml";
this._channel = new JChannel(xmlAbsPath);
關於 JGroups,目前我沒有進行“大消息包”和“大集羣量”的測試,還無法確定其性能表現。
如果性能方面表現良好,JGroups 放在遊戲項目中,實現跨服聊天、跨服 PK 及分佈式緩存,還是相當容易的。