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节点上即可。
注意:在上面提到的应用场景中,有个默认前提是:数据量很小,但是数据更新可能会比较快的场景。
- zookeeper应用场景-消息的订阅和发布
- ZooKeeper的典型应用场景之数据发布/订阅。
- zookeeper应用场景练习(数据发布/订阅)
- Redis发布订阅和应用场景
- Zookeeper 实现数据的发布和订阅
- Zookeeper实现数据的发布和订阅
- jedis的消息订阅和发布实例
- redis 消息订阅和发布
- Redis消息的发布/订阅
- Zookeeper的应用场景
- Zookeeper应用的场景
- Zookeeper应用的场景
- Zookeeper应用的场景
- zookeeper的应用场景
- ActiveMQ发布消息和订阅消息
- Java实现Redis的消息订阅和发布
- Java实现Redis的消息订阅和发布
- Java实现Redis的消息订阅和发布
- javascript 闭包的理解以及它的好处与坏处
- Spark 随机森林算法原理、源码分析及案例实战
- 关于控件滑动至顶部的监听
- 【UOJ22】. [UR #1]外星人
- 【消息队列MQ】各类MQ比较
- zookeeper应用场景-消息的订阅和发布
- javaweb域对象
- 面向对象设计原则之里氏代换原则
- unit12不同系统文本间的文件传输
- JavaScript之深入理解偏移量offset
- 【JavaEE】servlet技术
- Java:JFrame与Frame的区别
- 测试之路--Python
- Pandas标记删除重复记录