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

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

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

      消息的订阅和发布

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

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

zookeeper应用场景-消息的订阅和发布_第1张图片


代码

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 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 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;
	}
	
	@Override
	public 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;
	}
	@Override
	public 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 {

	//需要多少个workserver
	private 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  clients = new ArrayList();  
        //用来存储所有的workservers  
        List  workServers = new ArrayList();  
        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,分布式,配置中心,消息的订阅和发布)