Zookeeper学习(八):Zookeeper的数据发布与订阅模式
http://blog.csdn.net/ZuoAnYinXiang/article/category/6104448
1.发布订阅的基本概念
1.发布订阅模式可以看成一对多的关系:多个订阅者对象同时监听一个主题对象,这个主题对象在自身状态发生变化时,会通知所有的订阅者对象,使他们能够自动的更新自己的状态。
2.发布订阅模式,可以让发布方和订阅方,独立封装,独立改变,当一个对象的改变,需要同时改变其他的对象,而且它不知道有多少个对象需要改变时,可以使用发布订阅模式
3.发布订阅模式在分布式系统的典型应用有, 配置管理和服务发现。
配置管理:是指如果集群中机器拥有某些相同的配置,并且这些配置信息需要动态的改变,我们可以使用发布订阅模式,对配置文件做统一的管理,让这些机器各 自订阅配置文件的改变,当配置文件发生改变的时候这些机器就会得到通知,把自己的配置文件更新为最新的配置
服务发现:是指对集群中的服务上下线做统一的管理,每个工作服务器都可以作为数据的发布方,向集群注册自己的基本信息,而让模型机器作为订阅方,订阅工 作服务器的基本信息,当工作服务器的基本信息发生改变时如上下线,服务器的角色和服务范围变更,监控服务器就会得到通知,并响应这些变化。
2.发布订阅的架构图
3.Manager Server的工作流程
4.Work Server的工作流程
5.发布订阅程序的结构图
6.程序代码实现
- package com.zk.subscribe;
- 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;
- public class SubscribeZkClient {
- //需要多少个workserver
- private static final int CLIENT_QTY = 5;
- private static final String ZOOKEEPER_SERVER = "192.168.30.164:2181,192.168.30.165:2181,192.168.30.166: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) throws Exception
- {
- //用来存储所有的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();
- //根据定义的work服务个数,创建服务器后注册,然后启动
- 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(CONFIG_PATH, SERVERS_PATH, serverData, client, initConfig);
- workServers.add(workServer);
- workServer.start();
- }
- System.out.println("敲回车键退出!\n");
- new BufferedReader(new InputStreamReader(System.in)).readLine();
- }finally{
- //将workserver和client给关闭
- System.out.println("Shutting down...");
- for ( WorkServer workServer : workServers )
- {
- try {
- workServer.stop();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- for ( ZkClient client : clients )
- {
- try {
- client.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
- package com.zk.subscribe;
- import java.util.List;
- import org.I0Itec.zkclient.IZkChildListener;
- import org.I0Itec.zkclient.IZkDataListener;
- import org.I0Itec.zkclient.ZkClient;
- import org.I0Itec.zkclient.exception.ZkNoNodeException;
- import org.I0Itec.zkclient.exception.ZkNodeExistsException;
- import com.alibaba.fastjson.JSON;
- public class ManagerServer {
- private String serversPath;
- private String commandPath;
- private String configPath;
- private ZkClient zkClient;
- private ServerConfig config;
- //用于监听zookeeper中servers节点的子节点列表变化
- private IZkChildListener childListener;
- //用于监听zookeeper中command节点的数据变化
- private IZkDataListener dataListener;
- //工作服务器的列表
- private List<String> workServerList;
- /**
- *
- * @param serversPath
- * @param commandPath Zookeeper中存放命令的节点路径
- * @param configPath
- * @param zkClient
- * @param config
- */
- public ManagerServer(String serversPath, String commandPath,String configPath, ZkClient zkClient, ServerConfig config) {
- this.serversPath = serversPath;
- this.commandPath = commandPath;
- this.zkClient = zkClient;
- this.config = config;
- this.configPath = configPath;
- this.childListener = new IZkChildListener() {
- //用于监听zookeeper中servers节点的子节点列表变化
- public void handleChildChange(String parentPath,List<String> currentChilds) throws Exception {
- //更新服务器列表
- workServerList = currentChilds;
- System.out.println("work server list changed, new list is ");
- execList();
- }
- };
- //用于监听zookeeper中command节点的数据变化
- this.dataListener = new IZkDataListener() {
- public void handleDataDeleted(String dataPath) throws Exception {
- }
- public void handleDataChange(String dataPath, Object data)
- throws Exception {
- String cmd = new String((byte[]) data);
- System.out.println("cmd:"+cmd);
- exeCmd(cmd);
- }
- };
- }
- public void start() {
- initRunning();
- }
- public void stop() {
- //取消订阅command节点数据变化和servers节点的列表变化
- zkClient.unsubscribeChildChanges(serversPath, childListener);
- zkClient.unsubscribeDataChanges(commandPath, dataListener);
- }
- /**
- * 初始化
- */
- private void initRunning() {
- //执行订阅command节点数据变化和servers节点的列表变化
- zkClient.subscribeDataChanges(commandPath, dataListener);
- zkClient.subscribeChildChanges(serversPath, childListener);
- }
- /*
- * 执行控制命令的函数
- * 1: list 2: create 3: modify
- */
- private void exeCmd(String 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);
- }
- }
- private void execList() {
- System.out.println(workServerList.toString());
- }
- private void execCreate() {
- if (!zkClient.exists(configPath)) {
- try {
- zkClient.createPersistent(configPath, JSON.toJSONString(config).getBytes());
- } catch (ZkNodeExistsException e) {
- //节点已经存在异常,直接写入数据
- zkClient.writeData(configPath, JSON.toJSONString(config).getBytes());
- } catch (ZkNoNodeException e) {
- //表示其中的一个节点的父节点还没有被创建
- String parentDir = configPath.substring(0,configPath.lastIndexOf('/'));
- zkClient.createPersistent(parentDir, true);
- execCreate();
- }
- }
- }
- private void execModify() {
- config.setDbUser(config.getDbUser() + "_modify");
- try {
- //回写到zookeeper中
- zkClient.writeData(configPath, JSON.toJSONString(config).getBytes());
- } catch (ZkNoNodeException e) {
- execCreate();
- }
- }
- }
- package com.zk.subscribe;
- import org.I0Itec.zkclient.IZkChildListener;
- import org.I0Itec.zkclient.IZkDataListener;
- import org.I0Itec.zkclient.ZkClient;
- import org.I0Itec.zkclient.exception.ZkNoNodeException;
- import org.I0Itec.zkclient.exception.ZkNodeExistsException;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- /**
- * 代表工作服务器
- * workServer服务器的信息
- *
- */
- public class WorkServer{
- private String serversPath;
- private String configPath;
- private ZkClient zkClient;
- private ServerConfig config;
- private ServerData serverData;
- private IZkDataListener dataListener;//数据监听器
- /**
- *
- * @param configPath 代表config节点的路径
- * @param serversPath 代表servers节点的路径
- * @param serverData 代表当前服务器的基本信息
- * @param zkClient 底层与zookeeper集群通信的组件
- * @param initconfig 当前服务器的初始配置
- */
- public WorkServer(String configPath,String serversPath,ServerData serverData,ZkClient zkClient, ServerConfig initconfig){
- this.configPath = configPath;
- this.serversPath = serversPath;
- this.serverData = serverData;
- this.zkClient = zkClient;
- this.config = initconfig;
- /**
- * dataListener 用于监听config节点的数据改变
- */
- this.dataListener = new IZkDataListener() {
- public void handleDataDeleted(String arg0) throws Exception {
- }
- /**
- * 当数据的值改变时处理的
- * Object data,这个data是将ServerConfig对象转成json字符串存入
- * 可以通过参数中的Object data 拿到当前数据节点最新的配置信息
- * 拿到这个data信息后将它反序列化成ServerConfig对象,然后更新到自己的serverconfig属性中
- */
- public void handleDataChange(String dataPath, Object data) throws Exception {
- String retJson = new String((byte[])data);
- ServerConfig serverConfigLocal = (ServerConfig)JSON.parseObject(retJson,ServerConfig.class);
- //更新配置
- updateConfig(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);
- }
- /**
- * 启动时向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();
- }
- }
- /**
- * 当监听到zookeeper中config节点的配置信息改变时,要读取配置信息来更新自己的配置信息
- */
- private void updateConfig(ServerConfig serverConfig){
- this.config = serverConfig;
- }
- }
- package com.zk.subscribe;
- /**
- * 用于记录WorkServer(工作服务器)的配置信息
- */
- 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 + "]";
- }
- }
- package com.zk.subscribe;
- /**
- * 用于记录WorkServer(工作服务器)的基本信息
- */
- 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 + "]";
- }
- }
启动SubscribeZkClient
在zookeeper客户端上输出命令
managerServer订阅了commod的变化后,输出变化
Zookeeper学习(八):Zookeeper的数据发布与订阅模式的更多相关文章
- Zookeeper应用之一:数据发布与订阅初体验
Zookeeper到底是什么?可以从Zookeeper提供的功能来理解.本篇小作文就是使用其提供的功能之一:数据发布与订阅. 需求:服务端开启多个实例提供服务,客户端使用服务.如果服务端某个服务下线或 ...
- RabbitMQ学习笔记(三) 发布与订阅
发布与订阅 在我们使用手机发送消息的时候,即可以选择给单个手机号码发送消息,也可以选择多个手机号码,群发消息. 前面学习工作队列的时候,我们使用的场景是一个消息只能被一个消费者程序实例接收并处理,但是 ...
- node.js 中 events emitter 的实现(发布、订阅模式)
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('event', ...
- ZooKeeper 典型应用场景-数据发布与订阅
ZooKeeper 是一个高可用的分布式数据管理与系统协调框架.基于对 Paxos 算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得 ZooKeeper 可以解决很多分 ...
- ZooKeeper学习之-Zookeeper简单介绍(一)
一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同 ...
- zookeeper学习(2)----zookeeper和kafka的关系
转载: Zookeeper 在 Kafka 中的作用 leader 选举 和 follower 信息同步 如上图所示,kafaka集群的 broker,和 Consumer 都需要连接 Zookeep ...
- 知方可补不足~SQL2008中的发布与订阅模式
回到目录 作用:完成数据库与数据库的数据同步 原理:源数据库发布需要同时的表,存储过程,或者函数:目标数据库去订阅它,当源发生变化时,目标数据库自己同步,注意,由于这个过程是SQL自动完成的,所以要求 ...
- JS模式---发布、订阅模式
发布订阅模式又叫观察者模式,它定义一种一对多的依赖关系, 当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. document.body.addEventListener('click', ...
- Oracle学习(八):处理数据
1.知识点:能够对比以下的录屏进行阅读 SQL> --SQL语句 SQL> --1. DML语句(Data Manipulation Language 数据操作语言): insert up ...
随机推荐
- VMWARE TOOLS安装出错:THE PATH IS NOT A VALID PATH TO THE 3.11.0.12-GENERIC KERNEL HEADERS
VMWARE TOOLS安装提示THE PATH IS NOT A VALID PATH TO THE GENERIC KERNEL HEADERS I solved this problem, I ...
- iOS音频掌柜-- AVAudioSession
音频输出作为硬件资源,对于iOS系统来说是唯一的,那么要如何协调和各个App之间对这个稀缺的硬件持有关系呢? iOS给出的解决方案是"AVAudioSession" ,通过它可以实 ...
- 使用hibernate读取hibernate.cfg.xml文件时碰到这个错误org.hibernate.internal.util.config.ConfigurationException: Could not locate cfg.xml resource [/HibernateTest/src/hibernate.cfg.xml]
我的问题在于把hibernate.cfg.xml文件放置在某个包中了,hibernate.cfg.xml文件需要放置在src目录下.
- review12
使用StringTokenizer对象分解字符串,和split()不同的是,StringTokenizer对象不使用正则表达式作为分隔标记. 当Fenix一个字符串并将字符串分解成可被独立使用的单词时 ...
- review10
public int indexOf(String s); 字符串的索引位置时从0开始的(只有一个参数时),从当前字符串的头开始检索字符串s,并返回首次出现s的索引位置.如果没有检测到字符串s,该方法 ...
- php特级课---1、网站大访问量如何解决
php特级课---1.网站大访问量如何解决 一.总结 一句话总结: 负载均衡和冗余技术 1.负载均衡和冗余技术是一回事么? 并不是:负载均衡是用户分流:冗余技术是避免出现单点故障 负载均衡:将不同的用 ...
- CentOS 6.6 中 mysql_5.6 主从数据库配置
[mysql5.6 主从复制] 1.配置主从节点的服务配置文件 1.1.配置master节点 [mysqld] binlog-format=row log-bin=master-bin log-sla ...
- String随笔
1.古罗马皇帝凯撒在打仗时曾经使用过以下方法加密军事情报:,请编写一个程序,使用上述算法加密或解密用户输入的英文字串要求设计思想.程序流程图.源代码.结果截图. 设计思想:1)定义一个String类型 ...
- 算法练习3---水仙花数java版
所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身. java程序代码如下: public class ArithTest { public static void ...
- 51nod 1428 贪心
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1428 1428 活动安排问题 基准时间限制:1 秒 空间限制:13107 ...