ZooKeeper 典型应用场景-数据发布与订阅
ZooKeeper 是一个高可用的分布式数据管理与系统协调框架。基于对 Paxos 算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得 ZooKeeper 可以解决很多分布式问题。
随着互联网系统规模的不断扩大,大数据时代飞速到来,越来越多的分布式系统将 ZooKeeper 作为核心组件使用,如 Hadoop、Hbase、Kafka、Storm等,因此,正确理解 ZooKeeper 的应用场景,对于 ZooKeeper 的使用者来说显得尤为重要。本节主要将重点围绕数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等方面来讲解 ZooKeeper 的典型应用场景及实现。
1、数据发布/订阅
发布/订阅模式是一对多的关系,多个订阅者对象同时监听某一主题对象,这个主题对象在自身状态发生变化时会通知所有的订阅者对象。使它们能自动的更新自己的状态。发布/订阅可以使得发布方和订阅方独立封装、独立改变。当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象需要改变时可以使用发布/订阅模式。发布/订阅模式在分布式系统中的典型应用有配置管理和服务发现、注册。
配置管理是指如果集群中的机器拥有某些相同的配置并且这些配置信息需要动态的改变,我们可以使用发布/订阅模式把配置做统一集中管理,让这些机器格子各自订阅配置信息的改变,当配置发生改变时,这些机器就可以得到通知并更新为最新的配置。
服务发现、注册是指对集群中的服务上下线做统一管理。每个工作服务器都可以作为数据的发布方向集群注册自己的基本信息,而让某些监控服务器作为订阅方,订阅工作服务器的基本信息,当工作服务器的基本信息发生改变如上下线、服务器角色或服务范围变更,监控服务器可以得到通知并响应这些变化。
1.1、配置管理
所谓的配置中心,顾名思义就是发布者将数据发布到 ZooKeeper 的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
发布/订阅系统一般有两种设计模式,分别是推(Push)模式和拉(Pull)模式。
- 推模式
服务端主动将数据更新发送给所有订阅的客户端。
- 拉模式
客户端通过采用定时轮询拉取。
ZooKeeper采用的是推拉相结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
如果将配置信息存放到ZK上进行集中管理,那么通常情况下,应用在启动的时候会主动到ZK服务器上进行一次配置信息的获取,同时,在指定上注册一个Watcher监听,这样一来,但凡配置信息发生变更,服务器都会实时通知所有订阅的客户端,从而达到实时获取最新配置信息的目的。
下面我们通过一个“配置管理”的实际案例来展示ZK在“数据发布/订阅”场景下的使用方式。
在我们平常的应用系统开发中,经常会碰到这样的需求:系统中需要使用一些通用的配置信息,例如机器列表信息、运行时的开关配置、数据库的配置信息等。这些全局配置信息通常具备以下特性:
1)、数据量通常比较小
2)、数据内容在运行时会发生变化
3)、集群中各机器共享、配置一致
对于这类配置信息,一般的做法通常可以选择将其存储的本地配置文件或是内存变量中。无论采取哪种配置都可以实现相应的操作。但是一旦遇到集群规模比较大的情况的话,两种方式就不再可取。而我们还需要能够快速的做到全部配置信息的变更,同时希望变更成本足够小,因此我们需要一种更为分布式的解决方案。
接下来我们以“数据库切换”的应用场景展开,看看如何使用ZK来实现配置管理。
配置存储
在进行配置管理之前,首先我们需要将初始化配置存储到ZK上去,一般情况下,我们可以在ZK上选取一个数据节点用于配置的存储,我们将需要集中管理的配置信息写入到该数据节点中去。
配置获取
集群中每台机器在启动初始化阶段,首先会从上面提到的ZK的配置节点上读取数据库信息,同时,客户端还需要在该配置节点上注册一个数据变更的Watcher监听,一旦发生节点数据变更,所有订阅的客户端都能够获取数据变更通知。
配置变更
在系统运行过程中,可能会出现需要进行书局切换的情况,这个时候就需要进行配置变更。借助ZK,我们只需要对ZK上配置节点的内容进行更新,ZK就能够帮我们将数据变更的通知发送到各个客户端,每个客户端在接收到这个变更通知后,就可以重新进行最新数据的获取。
1.2、服务发现、注册
1.3、综合例子
架构图:
Manage Server 程序主体流程:
Work Server 程序主体流程:
系统的核心类:
1.4、程序代码实现
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 org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkNoNodeException; import com.alibaba.fastjson.JSON; public class WorkServer { private ZkClient zkClient; private String configPath; private String serversPath; private ServerData serverData; private ServerConfig serverConfig; private IZkDataListener dataListener; public WorkServer(String configPath, String serversPath, ServerData serverData, ZkClient zkClient, ServerConfig initConfig) { this.zkClient = zkClient; this.configPath = configPath; this.serversPath = serversPath; this.serverData = serverData; this.serverConfig = initConfig; this.dataListener = new IZkDataListener() { public void handleDataDeleted(String dataPath) throws Exception { } 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:" + serverConfig.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() { registMe(); zkClient.subscribeDataChanges(configPath, dataListener); } private void registMe() { String mePath = serversPath.concat("/").concat(serverData.getAddress()); try { zkClient.createEphemeral(mePath, JSON.toJSONString(serverData) .getBytes()); } catch (ZkNoNodeException e) { zkClient.createPersistent(serversPath, true); registMe(); } } private void updateConfig(ServerConfig serverConfig) { this.serverConfig = serverConfig; } }
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 ManageServer { private String serversPath; private String commandPath; private String configPath; private ZkClient zkClient; private ServerConfig config; private IZkChildListener childListener; private IZkDataListener dataListener; private List<String> workServerList; public ManageServer(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() { public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception { workServerList = currentChilds; System.out.println("work server list changed, new list is "); execList(); } }; this.dataListener = new IZkDataListener() { public void handleDataDeleted(String dataPath) throws Exception { // ignore; } public void handleDataChange(String dataPath, Object data) throws Exception { String cmd = new String((byte[]) data); System.out.println("cmd:" + cmd); exeCmd(cmd); } }; } private void initRunning() { 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 { zkClient.writeData(configPath, JSON.toJSONString(config).getBytes()); } catch (ZkNoNodeException e) { execCreate(); } } public void start() { initRunning(); } public void stop() { zkClient.unsubscribeChildChanges(serversPath, childListener); zkClient.unsubscribeDataChanges(commandPath, dataListener); } }
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import com.sql.zookeeper.common.ZookeeperConstant; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer; public class SubscribeZkClient { private static final int CLIENT_QTY = 5; 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 { List<ZkClient> clients = new ArrayList<ZkClient>(); List<WorkServer> workServers = new ArrayList<WorkServer>(); ManageServer manageServer = null; try { ServerConfig initConfig = new ServerConfig(); initConfig.setDbPwd("123456"); initConfig.setDbUrl("jdbc:mysql://localhost:3306/mydb"); initConfig.setDbUser("root"); ZkClient clientManage = new ZkClient(ZookeeperConstant.ZK_CONNECTION_STRING, 5000, 5000, new BytesPushThroughSerializer()); manageServer = new ManageServer(SERVERS_PATH, COMMAND_PATH, CONFIG_PATH, clientManage, initConfig); manageServer.start(); for (int i = 0; i < CLIENT_QTY; ++i) { ZkClient client = new ZkClient(ZookeeperConstant.ZK_CONNECTION_STRING, 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 { 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(); } } } } }
启动SubscribeZkClient
在zookeeper客户端上输出命令
managerServer订阅了commod的变化后,输出变化
http://www.cnblogs.com/sky-sql/p/6685531.html
相关推荐
### ZooKeeper典型使用场景详解 #### 一、概述 ZooKeeper是一款开源的分布式协调服务框架,主要用于解决分布式系统中的数据一致性问题。它基于Paxos算法实现,确保了即使在网络分区的情况下,也能保证分布式环境下...
尽管ZooKeeper最初并非为特定应用场景设计,但开发者们逐渐发掘出了一系列典型用途,利用其提供的API接口(原语集)来满足需求。 1. 数据发布与订阅(配置中心) ZooKeeper可以作为一个配置中心,允许发布者将数据...
ZooKeeper被广泛应用于解决多种分布式问题,以下是一些典型的ZooKeeper应用场景: 1. 数据发布与订阅(配置中心): ZooKeeper作为一个配置中心,允许发布者将数据发布到特定节点,订阅者则可以通过注册Watcher...
通过上述介绍的几个典型应用场景,可以看出ZooKeeper不仅能够简化分布式系统的设计和实现,还能够提高系统的稳定性和扩展性。对于正在构建或优化分布式应用的企业来说,掌握ZooKeeper的核心概念和技术细节是非常有...
4. 数据发布与订阅:Zookeeper支持发布/订阅模型,允许客户端订阅特定的数据路径,当这些数据发生变化时,Zookeeper会通知订阅者,实现数据的实时同步。 5. 负载均衡:在分布式环境中,Zookeeper可以协助实现负载...
#### 三、Zookeeper典型应用场景 ##### 1. 数据发布与订阅(配置中心) Zookeeper可以用于发布和订阅配置信息,实现配置信息的集中管理和动态更新。具体应用场景包括: - **全局配置管理**:将全局配置信息存储在...
下面我们将详细介绍ZooKeeper在实际应用场景中的几种典型用途。 #### 二、数据发布与订阅(配置中心) 在分布式系统中,往往需要对一些全局性的配置进行统一管理和动态更新,如服务地址列表、系统配置参数等。...
总结来说,Zookeeper通过提供一致性的命名服务、发布/订阅、配置管理、分布式同步等功能,成为构建复杂分布式系统的基石,广泛应用于大数据、微服务等领域。在实际应用中,开发者应根据具体业务需求,灵活运用...
本文将探讨 Zookeeper 的几个典型应用场景,并通过代码示例进行解析。 **统一命名服务 (Name Service)** 在分布式环境中,Zookeeper 提供了一种层次化的命名空间,类似于文件系统的目录结构。开发者可以通过调用 ...
除了上述应用场景,ZooKeeper还有其他用途: 4. 分布式锁 在分布式环境中,多个节点可能同时尝试执行某项操作,导致数据不一致。ZooKeeper提供了分布式锁机制,通过创建临时节点和监控父节点来实现锁的获取与释放,...
ZooKeeper的典型应用场景包括数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、分布式锁和分布式队列等。ZooKeeper的通知机制是指服务端在数据发生变更时,通知客户端的功能。 ZooKeeper和Dubbo都是...
数据发布/订阅是一种典型的应用场景,其中一方发布数据,而另一方通过订阅获取这些数据。Zookeeper支持混合模式的数据发布与订阅机制,既支持“推模式”也支持“拉模式”。发布者将数据发布到Zookeeper集群中,订阅...
Zookeeper的典型应用场景 ZooKeeper广泛应用于多种场景,包括但不限于: - **服务发现**:管理服务实例的注册和发现。 - **配置管理**:集中存储配置信息,便于统一管理。 - **分布式锁**:实现跨进程的互斥访问。...
#### Zookeeper 典型应用场景 1. **配置管理** 在分布式应用环境中,统一管理配置信息是非常重要的。Zookeeper 可以用来集中存储配置信息,并允许应用程序订阅这些配置的变更通知。当配置发生改变时,所有订阅的...
5. **数据发布与订阅**:Zookeeper 支持发布/订阅模式,允许节点发布数据,其他节点则可以订阅并接收这些数据的变化。 6. **状态同步**:Zookeeper 通过 Watcher 机制,可以实现实时监控数据的变化,从而在节点之间...
- 典型应用案例包括 HBase 中 Master 状态的监控与选举。 4. **分布式通知/协调** - 分布式环境中,一个服务往往需要了解它所管理的子服务的状态。例如,NameNode 需要知道各个 DataNode 的状态。 - 可以通过 ...
Zookeeper是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于Zookeeper实现诸如:分布式协调/通知,集群管理,Master选举, 分布式锁,分布式队列,命名服务,数据发布/订阅,负载均衡等功能. Zookeeper非常常用...
当 ZNode 中的数据发生变化时,Zookeeper 会通知所有订阅了该 ZNode 的节点。 - **1.4.3 统一集群管理**:在分布式环境中,Zookeeper 能够帮助实时监控每个节点的状态,以便于根据节点的状态变化来做出相应的决策...