zookeeper应用场景-消息的订阅和发布

来源:互联网 发布:数据库 schema 知乎 编辑:程序博客网 时间:2024/06/06 21:43

       Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储, Zookeeper 作用主要是用来维护和监控存储的数据的状态变化,通过监控这些数据状态的变化,从而达到基于数据的集群管理。

       zookeeper以其强大的灵活性著称,广泛应用于分布式集群的协调服务,首先简单介绍下zookeeper 主要应用场景之一,消息订阅和发布,下篇文章中我会详细介绍zookeeper另一个应用场景,负载均衡。

      消息的订阅和发布

       zookeeper的消息订阅及发布最典型的应用实例是作为系统的配置中心,发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息,服务式服务框架的服务地址列表等就非常适合使用。

       zookeeper 主要有两种监听方式,监听子节点状态监听节点数据。下图中,work server节点可存储应用服务器元数据(如:服务器ip和端口等信息),查询server节点可获取服务器列表,创建zookeeper客户端监听server节点的字节点状态,在应用服务器暂停使用(删除对应work server子节点)或增加应用服务器时(增加对应work server 子节点)时,zookeeper客户端可获及时的通知并进行处理。config节点可存储分布式集群的全局配置信息,在全局配置信息需要修改时,可将配置信息发布到config节点下,所有对config节点数据进行监听的zookeeper客户端可及时收到通知并对服务器的配置信息进行修改。


代码

managerServer主要监听commend节点的数据,及servers子节点的状态,在commend节点数据发生变化时,

将commend节点的数据发布的config 节点上。

import java.util.List;import org.I0Itec.zkclient.IZkChildListener;import org.I0Itec.zkclient.IZkDataListener;import org.I0Itec.zkclient.ZkClient;import com.alibaba.fastjson.JSON;public class ManagerServer {private String serversPath;private String commendPath;private String configPath;private ZkClient zkClient;private ServerConfig serverConfig;//用于监听zookeeper中servers节点的子节点列表变化private IZkChildListener childListener;//用于监听zookeeper中command节点的数据变化  private IZkDataListener dataListener;//工作服务器的列表      private List<String> workServerList;         public ManagerServer(String serversPath,String commendPath,String configPath,ZkClient zkClient,ServerConfig serverConfig){    this.serversPath=serversPath;    this.commendPath=commendPath;    this.configPath=configPath;    this.zkClient=zkClient;    this.serverConfig=serverConfig;    this.childListener=new IZkChildListener() {public void handleChildChange(String parentPath, List<String> currentChilds)throws Exception {System.out.println("work服务器列表发生变化");workServerList = currentChilds;execList();}};this.dataListener=new IZkDataListener() {public void handleDataDeleted(String dataPath) throws Exception {// TODO Auto-generated method stub}public void handleDataChange(String dataPath, Object data) throws Exception {System.out.println("------------------command节点数据发生改变------------------"); String cmdType = new String((byte[]) data);               System.out.println("cmd:"+cmdType);             if ("list".equals(cmdType)) {                   execList();                      } else if ("create".equals(cmdType)) {                   execCreate();               } else if ("modify".equals(cmdType)) {                   execModify();               } else {                   System.out.println("error command!" + cmdType);               }  }};    }    public void start() {          initRunning();      }        public void stop() {          //取消订阅command节点数据变化和servers节点的列表变化          zkClient.unsubscribeChildChanges(serversPath, childListener);          zkClient.unsubscribeDataChanges(commendPath, dataListener);      }            /**      * 初始化      */      private void initRunning() {          //执行订阅command节点数据变化和servers节点的列表变化         zkClient.subscribeDataChanges(commendPath, dataListener);         System.out.println("--------------managerServer监听command节点-------------");        zkClient.subscribeChildChanges(serversPath, childListener);        System.out.println("--------------managerServer监听服务器servers子节点-------------");          }          private void execModify() {    serverConfig.setDbUser(serverConfig.getDbUser() + "_modify");      zkClient.writeData(configPath, JSON.toJSONString(serverConfig).getBytes());    }        private void execCreate() {    if(!zkClient.exists(configPath)){    zkClient.createPersistent(configPath,JSON.toJSONString(serverConfig).getBytes());    }    }        private void execList() {     System.out.println(workServerList.toString());     }    }

workserver是监听config节点数据,在启动时到servers节点下注册服务器信息,并且监听config节点的数据变化.

import org.I0Itec.zkclient.IZkDataListener;import org.I0Itec.zkclient.ZkClient;import org.I0Itec.zkclient.exception.ZkException;import org.I0Itec.zkclient.exception.ZkInterruptedException;import org.I0Itec.zkclient.exception.ZkNoNodeException;import com.alibaba.fastjson.JSON;public class WorkServer {    private String serversPath;      private String configPath;      private ZkClient zkClient;      private ServerConfig config;      private ServerData serverData;    private IZkDataListener dataListener;//数据监听器          public WorkServer(String serversPath,String configPath,ZkClient zkClient,ServerConfig config,ServerData serverData){    this.serversPath=serversPath;    this.configPath=configPath;    this.zkClient=zkClient;    this.config=config;    this.serverData=serverData;        this.dataListener=new IZkDataListener() {public void handleDataDeleted(String dataPath) throws Exception {// TODO Auto-generated method stub}public void handleDataChange(String dataPath, Object data) throws Exception {String retJson=new String((byte[])data);ServerConfig serverConfigLocal = (ServerConfig)JSON.parseObject(retJson,ServerConfig.class);  updataConfig(serverConfigLocal);System.out.println("new work server config is:"+serverConfigLocal.toString());  }};    }            /**      * 服务的启动      */      public void start(){          System.out.println("work server start...");          initRunning();      }            /**      * 服务的停止      */      public void stop(){          System.out.println("work server stop...");          //取消监听          zkClient.unsubscribeDataChanges(configPath, dataListener);            }            /**      * 服务器的初始化      */      private void initRunning(){          registMeToZookeeper();          //订阅config节点的改变          zkClient.subscribeDataChanges(configPath, dataListener);         System.out.println("------------------------------监听config节点------------------------------");    }                private void updataConfig(ServerConfig serverConfigLocal) {    this.config=serverConfigLocal;    }        private void registMeToZookeeper(){    //向zookeeper中注册自己的过程其实就是向servers节点下注册一个临时节点          //构造临时节点          String mePath = serversPath.concat("/").concat(serverData.getAddress());       //存入是将json序列化          try {zkClient.createEphemeral(mePath, JSON.toJSONString(serverData).getBytes());System.out.println("--------------------创建worker服务器子节点 "+serverData.getAddress()+" -----------------------");} catch (ZkNoNodeException e) {//没有父节点则创建zkClient.createPersistent(serversPath, true);registMeToZookeeper();}         System.out.println("--------------------向子节点"+serverData.getAddress()+" 存入数据:"+JSON.toJSONString(serverData)+"-----------------------");    }    }



public class ServerConfig {private String dbUrl;private String dbPwd;private String dbUser;public String getDbUrl() {return dbUrl;}public void setDbUrl(String dbUrl) {this.dbUrl = dbUrl;}public String getDbPwd() {return dbPwd;}public void setDbPwd(String dbPwd) {this.dbPwd = dbPwd;}public String getDbUser() {return dbUser;}public void setDbUser(String dbUser) {this.dbUser = dbUser;}@Overridepublic String toString() {return "ServerConfig [dbUrl=" + dbUrl + ", dbPwd=" + dbPwd+ ", dbUser=" + dbUser + "]";}}



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;}@Overridepublic String toString() {return "ServerData [address=" + address + ", id=" + id + ", name="+ name + "]";}          }


import java.util.ArrayList;import java.util.List;import org.I0Itec.zkclient.ZkClient;import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;public class SubscribeZkClient {//需要多少个workserverprivate static final int CLIENT_QTY=5;private static final String ZOOKEEPER_SERVER="10.151.30.28:2181";//节点的路径      private static final String  CONFIG_PATH = "/config";//配置节点      private static final String  COMMAND_PATH = "/command";//命令节点      private static final String  SERVERS_PATH = "/servers";//服务器列表节点            public static void main(String[] args) {    //用来存储所有的clients          List<ZkClient>  clients = new ArrayList<ZkClient>();          //用来存储所有的workservers          List<WorkServer>  workServers = new ArrayList<WorkServer>();          ManagerServer manageServer = null;                try {ServerConfig initConfig = new ServerConfig();  initConfig.setDbPwd("123456");  initConfig.setDbUrl("jdbc:mysql://localhost:3306/mydb");  initConfig.setDbUser("root"); ZkClient clientManage =new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());manageServer=new ManagerServer(SERVERS_PATH, COMMAND_PATH, CONFIG_PATH, clientManage, initConfig);manageServer.start();for( int i = 0; i < CLIENT_QTY; ++i ){ZkClient client = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());   clients.add(client); ServerData serverData = new ServerData();       serverData.setId(i);       serverData.setName("WorkServer#"+i);       serverData.setAddress("192.168.1."+i);           WorkServer  workServer = new WorkServer(SERVERS_PATH, CONFIG_PATH, client, initConfig, serverData);     workServers.add(workServer);       workServer.start();}} catch (Exception e) {e.printStackTrace();}finally{manageServer.stop();for(WorkServer server:workServers){server.stop();}}    //    //向command节点下发指令//    ZkClient client=new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());////    client.createPersistent(COMMAND_PATH, true);//    ManagerServer manageServer=new ManagerServer(SERVERS_PATH, COMMAND_PATH, CONFIG_PATH, client, null);//manageServer.start();//try {//Thread.sleep(1000);//} catch (InterruptedException e) {//// TODO Auto-generated catch block//e.printStackTrace();//}//Object olddata = client.readData(COMMAND_PATH);//System.out.println(new String((byte[])olddata));//client.writeData(COMMAND_PATH, "create".getBytes());//Object data = client.readData(COMMAND_PATH);//System.out.println(new String((byte[])data));//try {//Thread.sleep(1000);//} catch (InterruptedException e) {//// TODO Auto-generated catch block//e.printStackTrace();//}//manageServer.stop();    }}


  • 应用中用到的一些配置信息放到ZK上进行集中管理。这类场景通常是这样:应用在启动的时候会主动来获取一次配置,同时,在节点上注册一个Watcher,这样一来,以后每次配置有更新的时候,都会实时通知到订阅的客户端,从来达到获取最新配置信息的目的。
  • 分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在ZK的一些指定节点,供各个客户端订阅使用。
  • 分布式日志收集系统。这个系统的核心工作是收集分布在不同机器的日志。收集器通常是按照应用来分配收集任务单元,因此需要在ZK上创建一个以应用名作为path的节点P,并将这个应用的所有机器ip,以子节点的形式注册到节点P上,这样一来就能够实现机器变动的时候,能够实时通知到收集器调整任务分配。
  • 系统中有些信息需要动态获取,并且还会存在人工手动去修改这个信息的发问。通常是暴露出接口,例如JMX接口,来获取一些运行时的信息。引入ZK之后,就不用自己实现一套方案了,只要将这些信息存放到指定的ZK节点上即可。

注意:在上面提到的应用场景中,有个默认前提是:数据量很小,但是数据更新可能会比较快的场景。



  

  

    



1 0