http://blog.csdn.net/ZuoAnYinXiang/article/category/6104448

1.发布订阅的基本概念

 

     1.发布订阅模式可以看成一对多的关系:多个订阅者对象同时监听一个主题对象,这个主题对象在自身状态发生变化时,会通知所有的订阅者对象,使他们能够自动的更新自己的状态。
 
     2.发布订阅模式,可以让发布方和订阅方,独立封装,独立改变,当一个对象的改变,需要同时改变其他的对象,而且它不知道有多少个对象需要改变时,可以使用发布订阅模式
 
    3.发布订阅模式在分布式系统的典型应用有, 配置管理和服务发现。
           配置管理:是指如果集群中机器拥有某些相同的配置,并且这些配置信息需要动态的改变,我们可以使用发布订阅模式,对配置文件做统一的管理,让这些机器各       自订阅配置文件的改变,当配置文件发生改变的时候这些机器就会得到通知,把自己的配置文件更新为最新的配置

服务发现:是指对集群中的服务上下线做统一的管理,每个工作服务器都可以作为数据的发布方,向集群注册自己的基本信息,而让模型机器作为订阅方,订阅工       作服务器的基本信息,当工作服务器的基本信息发生改变时如上下线,服务器的角色和服务范围变更,监控服务器就会得到通知,并响应这些变化。

 

2.发布订阅的架构


3.Manager Server的工作流程

4.Work Server的工作流程

5.发布订阅程序的结构图

 

 

 

 

6.程序代码实现


  1. package com.zk.subscribe;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import org.I0Itec.zkclient.ZkClient;
  7. import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
  8. public class SubscribeZkClient {
  9. //需要多少个workserver
  10. private static final int  CLIENT_QTY = 5;
  11. private static final String  ZOOKEEPER_SERVER = "192.168.30.164:2181,192.168.30.165:2181,192.168.30.166:2181";
  12. //节点的路径
  13. private static final String  CONFIG_PATH = "/config";//配置节点
  14. private static final String  COMMAND_PATH = "/command";//命令节点
  15. private static final String  SERVERS_PATH = "/servers";//服务器列表节点
  16. public static void main(String[] args) throws Exception
  17. {
  18. //用来存储所有的clients
  19. List<ZkClient>  clients = new ArrayList<ZkClient>();
  20. //用来存储所有的workservers
  21. List<WorkServer>  workServers = new ArrayList<WorkServer>();
  22. ManagerServer manageServer = null;
  23. try
  24. {
  25. ServerConfig initConfig = new ServerConfig();
  26. initConfig.setDbPwd("123456");
  27. initConfig.setDbUrl("jdbc:mysql://localhost:3306/mydb");
  28. initConfig.setDbUser("root");
  29. ZkClient clientManage = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());
  30. manageServer = new ManagerServer(SERVERS_PATH, COMMAND_PATH,CONFIG_PATH,clientManage,initConfig);
  31. manageServer.start();
  32. //根据定义的work服务个数,创建服务器后注册,然后启动
  33. for ( int i = 0; i < CLIENT_QTY; ++i )
  34. {
  35. ZkClient client = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer());
  36. clients.add(client);
  37. ServerData serverData = new ServerData();
  38. serverData.setId(i);
  39. serverData.setName("WorkServer#"+i);
  40. serverData.setAddress("192.168.1."+i);
  41. WorkServer  workServer = new WorkServer(CONFIG_PATH, SERVERS_PATH, serverData, client, initConfig);
  42. workServers.add(workServer);
  43. workServer.start();
  44. }
  45. System.out.println("敲回车键退出!\n");
  46. new BufferedReader(new InputStreamReader(System.in)).readLine();
  47. }finally{
  48. //将workserver和client给关闭
  49. System.out.println("Shutting down...");
  50. for ( WorkServer workServer : workServers )
  51. {
  52. try {
  53. workServer.stop();
  54. } catch (Exception e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. for ( ZkClient client : clients )
  59. {
  60. try {
  61. client.close();
  62. } catch (Exception e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. }
  67. }
  68. }
  1. package com.zk.subscribe;
  2. import java.util.List;
  3. import org.I0Itec.zkclient.IZkChildListener;
  4. import org.I0Itec.zkclient.IZkDataListener;
  5. import org.I0Itec.zkclient.ZkClient;
  6. import org.I0Itec.zkclient.exception.ZkNoNodeException;
  7. import org.I0Itec.zkclient.exception.ZkNodeExistsException;
  8. import com.alibaba.fastjson.JSON;
  9. public class ManagerServer {
  10. private String serversPath;
  11. private String commandPath;
  12. private String configPath;
  13. private ZkClient zkClient;
  14. private ServerConfig config;
  15. //用于监听zookeeper中servers节点的子节点列表变化
  16. private IZkChildListener childListener;
  17. //用于监听zookeeper中command节点的数据变化
  18. private IZkDataListener dataListener;
  19. //工作服务器的列表
  20. private List<String> workServerList;
  21. /**
  22. *
  23. * @param serversPath
  24. * @param commandPath Zookeeper中存放命令的节点路径
  25. * @param configPath
  26. * @param zkClient
  27. * @param config
  28. */
  29. public ManagerServer(String serversPath, String commandPath,String configPath, ZkClient zkClient, ServerConfig config) {
  30. this.serversPath = serversPath;
  31. this.commandPath = commandPath;
  32. this.zkClient = zkClient;
  33. this.config = config;
  34. this.configPath = configPath;
  35. this.childListener = new IZkChildListener() {
  36. //用于监听zookeeper中servers节点的子节点列表变化
  37. public void handleChildChange(String parentPath,List<String> currentChilds) throws Exception {
  38. //更新服务器列表
  39. workServerList = currentChilds;
  40. System.out.println("work server list changed, new list is ");
  41. execList();
  42. }
  43. };
  44. //用于监听zookeeper中command节点的数据变化
  45. this.dataListener = new IZkDataListener() {
  46. public void handleDataDeleted(String dataPath) throws Exception {
  47. }
  48. public void handleDataChange(String dataPath, Object data)
  49. throws Exception {
  50. String cmd = new String((byte[]) data);
  51. System.out.println("cmd:"+cmd);
  52. exeCmd(cmd);
  53. }
  54. };
  55. }
  56. public void start() {
  57. initRunning();
  58. }
  59. public void stop() {
  60. //取消订阅command节点数据变化和servers节点的列表变化
  61. zkClient.unsubscribeChildChanges(serversPath, childListener);
  62. zkClient.unsubscribeDataChanges(commandPath, dataListener);
  63. }
  64. /**
  65. * 初始化
  66. */
  67. private void initRunning() {
  68. //执行订阅command节点数据变化和servers节点的列表变化
  69. zkClient.subscribeDataChanges(commandPath, dataListener);
  70. zkClient.subscribeChildChanges(serversPath, childListener);
  71. }
  72. /*
  73. * 执行控制命令的函数
  74. * 1: list 2: create 3: modify
  75. */
  76. private void exeCmd(String cmdType) {
  77. if ("list".equals(cmdType)) {
  78. execList();
  79. } else if ("create".equals(cmdType)) {
  80. execCreate();
  81. } else if ("modify".equals(cmdType)) {
  82. execModify();
  83. } else {
  84. System.out.println("error command!" + cmdType);
  85. }
  86. }
  87. private void execList() {
  88. System.out.println(workServerList.toString());
  89. }
  90. private void execCreate() {
  91. if (!zkClient.exists(configPath)) {
  92. try {
  93. zkClient.createPersistent(configPath, JSON.toJSONString(config).getBytes());
  94. } catch (ZkNodeExistsException e) {
  95. //节点已经存在异常,直接写入数据
  96. zkClient.writeData(configPath, JSON.toJSONString(config).getBytes());
  97. } catch (ZkNoNodeException e) {
  98. //表示其中的一个节点的父节点还没有被创建
  99. String parentDir = configPath.substring(0,configPath.lastIndexOf('/'));
  100. zkClient.createPersistent(parentDir, true);
  101. execCreate();
  102. }
  103. }
  104. }
  105. private void execModify() {
  106. config.setDbUser(config.getDbUser() + "_modify");
  107. try {
  108. //回写到zookeeper中
  109. zkClient.writeData(configPath, JSON.toJSONString(config).getBytes());
  110. } catch (ZkNoNodeException e) {
  111. execCreate();
  112. }
  113. }
  114. }
  1. package com.zk.subscribe;
  2. import org.I0Itec.zkclient.IZkChildListener;
  3. import org.I0Itec.zkclient.IZkDataListener;
  4. import org.I0Itec.zkclient.ZkClient;
  5. import org.I0Itec.zkclient.exception.ZkNoNodeException;
  6. import org.I0Itec.zkclient.exception.ZkNodeExistsException;
  7. import com.alibaba.fastjson.JSON;
  8. import com.alibaba.fastjson.JSONObject;
  9. /**
  10. * 代表工作服务器
  11. * workServer服务器的信息
  12. *
  13. */
  14. public class WorkServer{
  15. private String serversPath;
  16. private String configPath;
  17. private ZkClient zkClient;
  18. private ServerConfig config;
  19. private ServerData serverData;
  20. private IZkDataListener dataListener;//数据监听器
  21. /**
  22. *
  23. * @param configPath 代表config节点的路径
  24. * @param serversPath 代表servers节点的路径
  25. * @param serverData   代表当前服务器的基本信息
  26. * @param zkClient     底层与zookeeper集群通信的组件
  27. * @param initconfig   当前服务器的初始配置
  28. */
  29. public WorkServer(String configPath,String serversPath,ServerData serverData,ZkClient zkClient, ServerConfig initconfig){
  30. this.configPath = configPath;
  31. this.serversPath = serversPath;
  32. this.serverData = serverData;
  33. this.zkClient = zkClient;
  34. this.config = initconfig;
  35. /**
  36. * dataListener 用于监听config节点的数据改变
  37. */
  38. this.dataListener = new IZkDataListener() {
  39. public void handleDataDeleted(String arg0) throws Exception {
  40. }
  41. /**
  42. * 当数据的值改变时处理的
  43. * Object data,这个data是将ServerConfig对象转成json字符串存入
  44. * 可以通过参数中的Object data 拿到当前数据节点最新的配置信息
  45. * 拿到这个data信息后将它反序列化成ServerConfig对象,然后更新到自己的serverconfig属性中
  46. */
  47. public void handleDataChange(String dataPath, Object data) throws Exception {
  48. String retJson = new String((byte[])data);
  49. ServerConfig serverConfigLocal = (ServerConfig)JSON.parseObject(retJson,ServerConfig.class);
  50. //更新配置
  51. updateConfig(serverConfigLocal);
  52. System.out.println("new work server config is:"+serverConfigLocal.toString());
  53. }
  54. };
  55. }
  56. /**
  57. * 服务的启动
  58. */
  59. public void start(){
  60. System.out.println("work server start...");
  61. initRunning();
  62. }
  63. /**
  64. * 服务的停止
  65. */
  66. public void stop(){
  67. System.out.println("work server stop...");
  68. //取消监听
  69. zkClient.unsubscribeDataChanges(configPath, dataListener);
  70. }
  71. /**
  72. * 服务器的初始化
  73. */
  74. private void initRunning(){
  75. registMeToZookeeper();
  76. //订阅config节点的改变
  77. zkClient.subscribeDataChanges(configPath, dataListener);
  78. }
  79. /**
  80. * 启动时向zookeeper注册自己
  81. */
  82. private void registMeToZookeeper(){
  83. //向zookeeper中注册自己的过程其实就是向servers节点下注册一个临时节点
  84. //构造临时节点
  85. String mePath = serversPath.concat("/").concat(serverData.getAddress());
  86. try{
  87. //存入是将json序列化
  88. zkClient.createEphemeral(mePath, JSON.toJSONString(serverData).getBytes());
  89. } catch (ZkNoNodeException e) {
  90. //父节点不存在
  91. zkClient.createPersistent(serversPath, true);
  92. registMeToZookeeper();
  93. }
  94. }
  95. /**
  96. * 当监听到zookeeper中config节点的配置信息改变时,要读取配置信息来更新自己的配置信息
  97. */
  98. private void updateConfig(ServerConfig serverConfig){
  99. this.config = serverConfig;
  100. }
  101. }
  1. package com.zk.subscribe;
  2. /**
  3. * 用于记录WorkServer(工作服务器)的配置信息
  4. */
  5. public class ServerConfig {
  6. private String dbUrl;
  7. private String dbPwd;
  8. private String dbUser;
  9. public String getDbUrl() {
  10. return dbUrl;
  11. }
  12. public void setDbUrl(String dbUrl) {
  13. this.dbUrl = dbUrl;
  14. }
  15. public String getDbPwd() {
  16. return dbPwd;
  17. }
  18. public void setDbPwd(String dbPwd) {
  19. this.dbPwd = dbPwd;
  20. }
  21. public String getDbUser() {
  22. return dbUser;
  23. }
  24. public void setDbUser(String dbUser) {
  25. this.dbUser = dbUser;
  26. }
  27. @Override
  28. public String toString() {
  29. return "ServerConfig [dbUrl=" + dbUrl + ", dbPwd=" + dbPwd + ", dbUser=" + dbUser + "]";
  30. }
  31. }
  1. package com.zk.subscribe;
  2. /**
  3. * 用于记录WorkServer(工作服务器)的基本信息
  4. */
  5. public class ServerData {
  6. private String address;
  7. private Integer id;
  8. private String name;
  9. public String getAddress() {
  10. return address;
  11. }
  12. public void setAddress(String address) {
  13. this.address = address;
  14. }
  15. public Integer getId() {
  16. return id;
  17. }
  18. public void setId(Integer id) {
  19. this.id = id;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. @Override
  28. public String toString() {
  29. return "ServerData [address=" + address + ", id=" + id + ", name=" + name + "]";
  30. }
  31. }

启动SubscribeZkClient


在zookeeper客户端上输出命令

managerServer订阅了commod的变化后,输出变化


 

 

 

 

 

 

 

 

Zookeeper学习(八):Zookeeper的数据发布与订阅模式的更多相关文章

  1. Zookeeper应用之一:数据发布与订阅初体验

    Zookeeper到底是什么?可以从Zookeeper提供的功能来理解.本篇小作文就是使用其提供的功能之一:数据发布与订阅. 需求:服务端开启多个实例提供服务,客户端使用服务.如果服务端某个服务下线或 ...

  2. RabbitMQ学习笔记(三) 发布与订阅

    发布与订阅 在我们使用手机发送消息的时候,即可以选择给单个手机号码发送消息,也可以选择多个手机号码,群发消息. 前面学习工作队列的时候,我们使用的场景是一个消息只能被一个消费者程序实例接收并处理,但是 ...

  3. node.js 中 events emitter 的实现(发布、订阅模式)

    const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('event', ...

  4. ZooKeeper 典型应用场景-数据发布与订阅

    ZooKeeper 是一个高可用的分布式数据管理与系统协调框架.基于对 Paxos 算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得 ZooKeeper 可以解决很多分 ...

  5. ZooKeeper学习之-Zookeeper简单介绍(一)

    一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同 ...

  6. zookeeper学习(2)----zookeeper和kafka的关系

    转载: Zookeeper 在 Kafka 中的作用 leader 选举 和 follower 信息同步 如上图所示,kafaka集群的 broker,和 Consumer 都需要连接 Zookeep ...

  7. 知方可补不足~SQL2008中的发布与订阅模式

    回到目录 作用:完成数据库与数据库的数据同步 原理:源数据库发布需要同时的表,存储过程,或者函数:目标数据库去订阅它,当源发生变化时,目标数据库自己同步,注意,由于这个过程是SQL自动完成的,所以要求 ...

  8. JS模式---发布、订阅模式

    发布订阅模式又叫观察者模式,它定义一种一对多的依赖关系, 当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. document.body.addEventListener('click', ...

  9. Oracle学习(八):处理数据

    1.知识点:能够对比以下的录屏进行阅读 SQL> --SQL语句 SQL> --1. DML语句(Data Manipulation Language 数据操作语言): insert up ...

随机推荐

  1. Flume架构及运行机制

    flume 作为 cloudera 开发的实时日志收集系统,受到了业界的认可与广泛应用.Flume 初始的发行版本目前被统称为 Flume OG(original generation),属于 clo ...

  2. windows编译hadoop 2.x Hadoop-eclipse-plugin插件

    本文转载至:http://blog.csdn.net/congcong68/article/details/42098391 一.简介 Hadoop2.x之后没有Eclipse插件工具,我们就不能在E ...

  3. Linux嵌入式 -- Bootloader , Uboot

    1. Bootloader作用 PC机中的引导加载程序由BIOS(其本质是一段固件程序)和GRUB或LILO一起组成.BIOS在完成硬件检测和资源分配后,将硬盘中的引导程序读到系统内存中然后将控制权交 ...

  4. JavaScript 对时间日期格式化

    JavaScript 对时间日期格式化 // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位 ...

  5. C# 实现WinForm窗口最小化到系统托盘代码

    1.如果不想让程序在任务栏中显示,请把窗体的属性ShowInTaskbar设置为false; 2.如果想让程序启动时就最小化,请设置窗体的属性WindowState设置为Minimized.(Mini ...

  6. review36

    对于Thread(Runnable target)构造方法创建的线程,轮到它来享用CPU资源时,目标对象就会自动调用接口中的run()方法,因此,对于使用同一目标对象的线程,目标对象的成员变量自然就是 ...

  7. OSGi类加载问题

    项目中遇到的JVM难点 ——启动OSGi容器时,出现永久代内存不够.内存泄露 ——OSGi找不到类路径问题. ——线程死锁问题.   问题一:OSGi类内存问题         其次,从内存用量来看, ...

  8. java: i18n语言

    <%@ page language="java" contentType="text/html; charset=utf8"%> <%@ pa ...

  9. node-并发控制

    当我们在做一些爬虫小程序的时候,如果我们一次性爬的数据条较多,那么相关软件也许会有限制或者是认为我们是非法的.那么我们就需要一些机制去限制获取数据的条数.而且node为我们提供的并发获取数据都是异步的 ...

  10. Xcode 查找 TODO 清单

    在 Xcode 中按 Shift+Command+F,显示在项目中查找窗口,选择按正则表达式查找(Find > Regular Expression): TODO:  //\s*\bTODO\s ...