在實際的生產環境中我們一般都是集群環境部署的,同一個程序我們會部署在相同的幾台服務器中,這時我們可以通過負載均衡服務器去調度,但是我們並不能很快速的獲知哪台服務器掛掉了,這時我們就可以使用zookeeper來解決這個問題。
zookeeper的動態感知
動態感知其實利用的就是zookeeper的watch功能,我們先來看下常規的負載均衡服務器的結構
再來看下我們用zookeeper實現的結構
文字描述:
1.感知上線
當服務器啟動的時候通過程序知道后會同時在zookeeper的servers節點下創建一個新的短暫有序節點來存儲當前服務器的信息。客戶端通過對servers節點的watch可以立馬知道有新的服務器上線了
2.感知下線
當我們有個服務器下線后,對應的servers下的短暫有序節點會被刪除,此時watch servers節點的客戶端也能立馬知道哪個服務器下線了,能夠及時將訪問列表中對應的服務器信息移除,從而實現及時感知服務器的變化。
代碼實現
服務器端代碼
package com.dpb.dynamic;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
/**
* 服務器端代碼
* @author dengp
*
*/
public class DistributedServer {
private static final String connectString = "192.168.88.121:2181,192.168.88.122:2181,192.168.88.123:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers";
private ZooKeeper zk = null;
/**
* 創建到zk的客戶端連接
*
* @throws Exception
*/
public void getConnect() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回調函數(應該是我們自己的事件處理邏輯)
System.out.println(event.getType() + "---" + event.getPath());
try {
zk.getChildren("/", true);
} catch (Exception e) {
}
}
});
}
/**
* 向zk集群注冊服務器信息
*
* @param hostname
* @throws Exception
*/
public void registerServer(String hostname) throws Exception {
String create = zk.create(parentNode + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + "is online.." + create);
}
/**
* 業務功能
*
* @throws InterruptedException
*/
public void handleBussiness(String hostname) throws InterruptedException {
System.out.println(hostname + "start working.....");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
// 獲取zk連接
DistributedServer server = new DistributedServer();
server.getConnect();
// 利用zk連接注冊服務器信息
server.registerServer(args[0]);
// 啟動業務功能
server.handleBussiness(args[0]);
}
}
客戶端代碼
package com.dpb.dynamic;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
/**
* 客戶端:通過zookeeper獲取服務器地址
* @author 波波烤鴨
*
*/
public class DistributedClient {
private static final String connectString = "192.168.88.121:2181,192.168.88.122:2181,192.168.88.123:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers";
// 注意:加volatile的意義何在?
private volatile List<String> serverList;
private ZooKeeper zk = null;
/**
* 創建到zk的客戶端連接
*
* @throws Exception
*/
public void getConnect() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回調函數(應該是我們自己的事件處理邏輯)
try {
//重新更新服務器列表,並且注冊了監聽
getServerList();
} catch (Exception e) {
}
}
});
}
/**
* 獲取服務器信息列表
*
* @throws Exception
*/
public void getServerList() throws Exception {
// 獲取服務器子節點信息,並且對父節點進行監聽
List<String> children = zk.getChildren(parentNode, true);
// 先創建一個局部的list來存服務器信息
List<String> servers = new ArrayList<String>();
for (String child : children) {
// child只是子節點的節點名
byte[] data = zk.getData(parentNode + "/" + child, false, null);
servers.add(new String(data));
}
// 把servers賦值給成員變量serverList,已提供給各業務線程使用
serverList = servers;
//打印服務器列表
System.out.println(serverList);
}
/**
* 業務功能
*
* @throws InterruptedException
*/
public void handleBussiness() throws InterruptedException {
System.out.println("client start working.....");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
// 獲取zk連接
DistributedClient client = new DistributedClient();
client.getConnect();
// 獲取servers的子節點信息(並監聽),從中獲取服務器信息列表
client.getServerList();
// 業務線程啟動
client.handleBussiness();
}
}
測試
1.在zookeeper中的/下創建一個servers永久節點
[zk: localhost:2181(CONNECTED) 1] create /servers servers
Created /servers
1.啟動三個服務器
再啟動另外兩個
3.啟動一個客戶端
4.關閉一個服務器然后在新開一個服務器觀察
關掉server01后客戶端立馬打印如下信息
更新了服務器列表,移除了server01
再開啟一個server04服務器查看
客戶端獲取到了剛剛上線的服務器。
ok~本文到此結束。