通過zookeeper完成動態感知分佈式服務器上下線的功能
業務描述
- 某分佈式系統中,主節點可以有多臺,可以動態上下線,任意一臺客戶端都能實時感知到主節點服務器的上下線。當主節點下線的時候,客戶端會收到通知,並更新目前在還在線的主節點host信息,可以防止客戶端向已經掛掉的節點進行請求。
服務器端的實現
要想完成上述功能,在我們可以想到通過zookeeper的短暫態的節點完成,在服務器啓動的時候,我們連接到zookeeper並向其註冊一個短暫態的節點,當服務器因爲某種意外宕機的時候,這個節點也會被刪除,這樣客戶端訪問所有的註冊節點的信息,就是仍然在正常工作的主節點。
服務器端的代碼如下:
import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; public class ClusterServer { //zookeeper的ip與端口,這裏是zookeeper是一個集羣 private String ZK_SERVER_LIST = "mini1:2181,mini2:2181,mini3:2181"; //設置超時時間,當主節點2s後沒反應就認該節點已經掛了 private static final int sessionTimeout = 2000; //所有的註冊節點信息當道/servers下面,方便管理 private String SERVER_DIR = "/servers"; private ZooKeeper zk; public void connect() throws IOException { zk = new ZooKeeper(ZK_SERVER_LIST, sessionTimeout, new Watcher() { //當zookeeper服務器集羣有斷線時會調用 @Override public void process(WatchedEvent event) { // TODO Auto-generated method stub System.out.println(event.toString()); } }); } public void register(String hontname) throws IOException, KeeperException, InterruptedException { //創建瞬時態且帶序號的節點,這樣在節點下線後該節點就會被刪除 String result = zk.create(SERVER_DIR+"/server", hontname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println("創建結果:"+result); } public void diy(){ System.out.println("just do it"); try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) throws IOException, KeeperException, InterruptedException { ClusterServer server =new ClusterServer(); server.connect(); server.register(args[0]); server.diy(); } }
程序啓動的時候就向服務器把當前主節點的host信息註冊到zookeeper中。
這裏的hostname本來可以通過java api進行動態獲取的,不過爲了方便實驗就省略了。
客戶端的實現
客戶端所需要的工作是獲取正常工作的主節點並且當主節點發生變化的時候可以收到信息,獲取最新的正常工作的主節點。所以我們可以對
servers
下面的子節點信息進行監聽,代碼實現
import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; public class ClusterClient { private String ZK_SERVER_LIST = "mini1:2181,mini2:2181,mini3:2181"; private static final int sessionTimeout = 2000; private String SERVER_DIR = "/servers"; private ZooKeeper zk; private List<String> servers=new ArrayList<>(); public void connect() throws IOException { zk = new ZooKeeper(ZK_SERVER_LIST, sessionTimeout, new Watcher() { @Override public void process(WatchedEvent event) { System.out.println(event.toString()); try { //收到通知後再次對服務器設置監聽。 getServerList(); System.out.println(servers); } catch (KeeperException | InterruptedException e) { } } }); } //獲取服務器列表並設置監聽 public void getServerList() throws KeeperException, InterruptedException{ List<String> nodeList = zk.getChildren(SERVER_DIR,true); servers.clear(); for(String node:nodeList){ byte []data = zk.getData(SERVER_DIR+"/"+node, false, null); String hostName = new String(data); servers.add(hostName); } } //做自己愛做的事同時完成守護進程的功能 public void diy(){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true){ } } }).start();; } public static void main(String[] args) throws IOException, KeeperException, InterruptedException { ClusterClient client =new ClusterClient(); client.connect(); client.getServerList(); client.diy(); } }
測試
我們可以在多個終端分別運行服務器程序,用來模擬多個主節點,直接關閉終端模仿主節點的宕機。
實驗結果如下(當我們動態增加節點的結果):
所有需要的jar包在zookeeper的解壓包裏面都有。或者直接在我的github裏面有代碼以及jar包。