發佈訂閱模式在分佈式系統的典型應用有, 配置管理和服務發現。
配置管理:是指如果集羣中機器擁有某些相同的配置,並且這些配置信息需要動態的改變,我們可以使用發佈訂閱模式,對配置文件做統一的管理,讓這些機器各自訂閱配置文件的改變,當配置文件發生改變的時候這些機器就會得到通知,把自己的配置文件更新爲最新的配置
服務發現:是指對集羣中的服務上下線做統一的管理,每個工作服務器都可以作爲數據的發佈方,向集羣註冊自己的基本信息,而讓模型機器作爲訂閱方,訂閱工作服務器的基本信息,當工作服務器的基本信息發生改變時如上下線,服務器的角色和服務範圍變更,監控服務器就會得到通知,並響應這些變化。
以服務發現的形式實現代碼:
package com.tlk.zk.chapter5.configCenter;
/**
* 用於記錄工作服務器的基本信息
*/
public class ServerData {
private String address;
private Integer id;
private String name;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ServerData [address=" + address + ", id=" + id + ", name=" + name + "]";
}
}
package com.tlk.zk.chapter5.configCenter;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import com.alibaba.fastjson.JSON;
/**
* 服務提供者,註冊服務到serversPath下
* @author tanlk
* @date 2017年8月13日 下午3:51:54
*/
public class Provider {
private String serversPath;
private ZkClient zkClient;
private ServerData serverData;
public Provider() {
}
/**
*
* @param serversPath provider要存的地址
* @param zkClient ZooKeeper連接
* @param serverData provider要存的信息
*/
public Provider(String serversPath, ZkClient zkClient, ServerData serverData) {
super();
this.serversPath = serversPath;
this.zkClient = zkClient;
this.serverData = serverData;
}
/**
* 服務的啓動
*/
public void start(){
System.out.println("provider server start...");
initRunning();
}
/**
* 服務器的初始化
*/
private void initRunning(){
registMeToZookeeper();
}
/**
* 啓動時向zookeeper註冊自己
*/
private void registMeToZookeeper(){
//向zookeeper中註冊自己的過程其實就是向servers節點下注冊一個臨時節點
//構造臨時節點
String mePath = serversPath.concat("/").concat(serverData.getAddress());
try{
//存入是將json序列化
zkClient.createEphemeral(mePath, JSON.toJSONString(serverData).getBytes());
} catch (ZkNoNodeException e) {
//父節點不存在
zkClient.createPersistent(serversPath, true);
registMeToZookeeper();
}
}
}
package com.tlk.zk.chapter5.configCenter;
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
/**
* 服務消費者,監聽serversPath的子節點的變化,有就更新
* @author tanlk
* @date 2017年8月13日 下午3:59:00
*/
public class Consumer {
private String serversPath;
private ZkClient zkClient;
//用於監聽zookeeper中servers節點的子節點列表變化
private IZkChildListener childListener;
//provider的列表
private List<String> providerServerList;
public Consumer() {
}
public Consumer(String serversPath, ZkClient zkClient) {
super();
this.serversPath = serversPath;
this.zkClient = zkClient;
this.childListener = new IZkChildListener() {
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
providerServerList = currentChilds;
System.out.println("provider server list changed, new list is ");
execList();
}
};
}
private void execList() {
System.out.println(providerServerList.toString());
}
public void start() {
initRunning();
}
public void stop() {
//取消訂閱servers節點的列表變化
zkClient.unsubscribeChildChanges(serversPath, childListener);
}
/**
* 初始化
*/
private void initRunning() {
//執行訂閱servers節點的列表變化
zkClient.subscribeChildChanges(serversPath, childListener);
}
}
package com.tlk.zk.chapter5.configCenter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
/**
* 測試類
* 先啓動一個consumer監聽SERVERS_PATH的子節點
* 再一個個的啓動provider查看consumer的監聽情況
*
* @author tanlk
* @date 2017年8月13日 下午4:07:08
*/
public class ConfigServerMain {
private static final String ZOOKEEPER_SERVER = "127.0.0.1:2181";
private static final String SERVERS_PATH = "/servers";
public static void main(String[] args) {
//用來存儲所有的clients,最後close使用
List<ZkClient> clients = new ArrayList<ZkClient>();
Consumer consumer = null;
try {
ZkClient clientManage = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());
consumer = new Consumer(SERVERS_PATH,clientManage);
consumer.start();
for(int i = 0; i < 5; i++){
ZkClient client = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());
clients.add(client);
ServerData serverData = new ServerData();
serverData.setAddress("192.168.1." +i);
serverData.setId(i);
serverData.setName("provider&&" + i);
Provider provider = new Provider(SERVERS_PATH, client, serverData);
provider.start();
System.out.println("敲回車鍵繼續添加provider!\n");
new BufferedReader(new InputStreamReader(System.in)).readLine();
}
System.out.println("敲回車鍵退出!\n");
new BufferedReader(new InputStreamReader(System.in)).readLine();
} catch (Exception e) {
}finally {
for (ZkClient zkClient : clients) {
zkClient.close();
}
}
}
}
ps:如果實現配置管理模式,只需要有個配置提供者修改配置參數(對應的是當前節點的值),配置使用着去監聽節點的數據變化(IZkDataListener),而非上訴的監聽子節點的數據變化(IZkChildListener)