在实际的生产环境中我们一般都是集群环境部署的, 同一个程序我们会部署在相同的几台服务器中, 这时我们可以通过负载均衡服务器去调度, 但是我们并不能很快速的获知哪台服务器挂掉了, 这时我们就可以使用 zookeeper 来解决这个问题.
zookeeper 的动态感知
动态感知其实利用的就是 zookeeper 的 watch 功能, 我们先来看下常规的负载均衡服务器的结构
再来看下我们用 zookeeper 实现的结构
文字描述:
1. 感知上线
当服务器启动的时候通过程序知道后会同时在 zookeeper 的 servers 节点下创建一个新的短暂有序节点来存储当前服务器的信息. 客户端通过对 servers 节点的 watch 可以立马知道有新的服务器上线了
2. 感知下线
当我们有个服务器下线后, 对应的 servers 下的短暂有序节点会被删除, 此时 watch servers 节点的客户端也能立马知道哪个服务器下线了, 能够及时将访问列表中对应的服务器信息移除, 从而实现及时感知服务器的变化.
代码实现
服务器端代码
- package com.dpb.dynamic;
- import org.apache.zookeeper.CreateMode;
- import org.apache.zookeeper.WatchedEvent;
- import org.apache.zookeeper.Watcher;
- import org.apache.zookeeper.ZooDefs.Ids;
- import org.apache.zookeeper.ZooKeeper;
- /**
- * 服务器端代码
- * @author dengp
- *
- */
- public class DistributedServer {
- private static final String connectString = "192.168.88.121:2181,192.168.88.122:2181,192.168.88.123:2181";
- private static final int sessionTimeout = 2000;
- private static final String parentNode = "/servers";
- private ZooKeeper zk = null;
- /**
- * 创建到 zk 的客户端连接
- *
- * @throws Exception
- */
- public void getConnect() throws Exception {
- zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
- @Override
- public void process(WatchedEvent event) {
- // 收到事件通知后的回调函数 (应该是我们自己的事件处理逻辑)
- System.out.println(event.getType() + "---" + event.getPath());
- try {
- zk.getChildren("/", true);
- } catch (Exception e) {
- }
- }
- });
- }
- /**
- * 向 zk 集群注册服务器信息
- *
- * @param hostname
- * @throws Exception
- */
- public void registerServer(String hostname) throws Exception {
- String create = zk.create(parentNode + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
- System.out.println(hostname + "is online.." + create);
- }
- /**
- * 业务功能
- *
- * @throws InterruptedException
- */
- public void handleBussiness(String hostname) throws InterruptedException {
- System.out.println(hostname + "start working.....");
- Thread.sleep(Long.MAX_VALUE);
- }
- public static void main(String[] args) throws Exception {
- // 获取 zk 连接
- DistributedServer server = new DistributedServer();
- server.getConnect();
- // 利用 zk 连接注册服务器信息
- server.registerServer(args[0]);
- // 启动业务功能
- server.handleBussiness(args[0]);
- }
- }
客户端代码
- package com.dpb.dynamic;
- import java.util.ArrayList;
- import java.util.List;
- import org.apache.zookeeper.WatchedEvent;
- import org.apache.zookeeper.Watcher;
- import org.apache.zookeeper.ZooKeeper;
- /**
- * 客户端: 通过 zookeeper 获取服务器地址
- * @author 波波烤鸭
- *
- */
- public class DistributedClient {
- private static final String connectString = "192.168.88.121:2181,192.168.88.122:2181,192.168.88.123:2181";
- private static final int sessionTimeout = 2000;
- private static final String parentNode = "/servers";
- // 注意: 加 volatile 的意义何在?
- private volatile List<String> serverList;
- private ZooKeeper zk = null;
- /**
- * 创建到 zk 的客户端连接
- *
- * @throws Exception
- */
- public void getConnect() throws Exception {
- zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
- @Override
- public void process(WatchedEvent event) {
- // 收到事件通知后的回调函数 (应该是我们自己的事件处理逻辑)
- try {
- // 重新更新服务器列表, 并且注册了监听
- getServerList();
- } catch (Exception e) {
- }
- }
- });
- }
- /**
- * 获取服务器信息列表
- *
- * @throws Exception
- */
- public void getServerList() throws Exception {
- // 获取服务器子节点信息, 并且对父节点进行监听
- List<String> children = zk.getChildren(parentNode, true);
- // 先创建一个局部的 list 来存服务器信息
- List<String> servers = new ArrayList<String>();
- for (String child : children) {
- // child 只是子节点的节点名
- byte[] data = zk.getData(parentNode + "/" + child, false, null);
- servers.add(new String(data));
- }
- // 把 servers 赋值给成员变量 serverList, 已提供给各业务线程使用
- serverList = servers;
- // 打印服务器列表
- System.out.println(serverList);
- }
- /**
- * 业务功能
- *
- * @throws InterruptedException
- */
- public void handleBussiness() throws InterruptedException {
- System.out.println("client start working.....");
- Thread.sleep(Long.MAX_VALUE);
- }
- public static void main(String[] args) throws Exception {
- // 获取 zk 连接
- DistributedClient client = new DistributedClient();
- client.getConnect();
- // 获取 servers 的子节点信息 (并监听), 从中获取服务器信息列表
- client.getServerList();
- // 业务线程启动
- client.handleBussiness();
- }
- }
测试
1. 在 zookeeper 中的 / 下创建一个 servers 永久节点
- [zk: localhost:2181(CONNECTED) 1] create /servers servers
- Created /servers
1. 启动三个服务器
再启动另外两个
3. 启动一个客户端
4. 关闭一个服务器然后在新开一个服务器观察
关掉 server01 后客户端立马打印如下信息
更新了服务器列表, 移除了 server01
再开启一个 server04 服务器查看
客户端获取到了刚刚上线的服务器.
ok~ 本文到此结束.
来源: https://www.cnblogs.com/dengpengbo/p/10443547.html