用ZK来实现分布式应用系统服务器上下线动态感知的功能

我们知道ZK有个功能就是 动态感知服务器的正常运行,

比如说:A是客户端,B、C、D都是应用服务器,一共有三台;每次请求,客户端A都去调用BCD里面的一台,如果BCD里有一

台服务器B挂掉的话,ZK感知到并告诉A下次请求C和D服务器,从而保证程序正常运行。

那我们就来用代码简单实现一下功能吧。

/*
 * Project: blog.web
 * 
 * File Created at 2018年5月26日
 * 
 * Copyright 2018 CMCC Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * ZYHY Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license.
 */
package com.maple.blog.web.zk;

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;
import org.apache.zookeeper.data.Stat;

/**
 * @Type Server.java
 * @Desc 我们知道ZK有个功能就是监听,父节点会监听子节点的运行状态,如果有一个子节点挂了,父节点有感知功能。
 * 那现在我们就来简单开发一个 动态感知服务器上下线功能
 * @author 王弘博
 * @date 2018年5月26日 下午8:53:53
 * @version 
 */
public class DistributedServer {

    private static final String connectString = "192.168.116.128:2181,192.168.116.129:2181,192.168.116.130:2181";
    private static final int sessionTimeout = 2000;
    private static final String parentNode = "/servers";
    private static final String parentData = "zkserver";

    ZooKeeper zkClient = null;

    /**
     * 获取到ZK客户端的连接
     * @throws Exception
     */
    public void getConnect() throws Exception {
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                //收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
                System.out.println(event.getType() + "----" + event.getPath());
                try {
                    zkClient.getChildren("/", true);//再次触发监听
                } catch (Exception e) {
                }
            }
        });
    }
    
    /**
     * 向ZK集群注册服务器信息
     * @param hostname
     * @throws Exception
     */
    public void registerServer(String hostname) throws Exception {
        //去根目录下创建一个 /servers 
        Stat stat = zkClient.exists(parentNode, false);
        if(null == stat) {
            zkClient.create(parentNode, parentData.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        String create = zkClient.create(parentNode+"/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname+" is online.... "+create);
    }
    
    /**
     * 业务功能
     * @throws Exception 
     */
    public void handleBussiness(String hostname) throws Exception {
        System.out.println(hostname + " start working.........");
        Thread.sleep(Long.MAX_VALUE);
    }
    
    public static void main(String[] args) throws Exception {
        
        DistributedServer server = new DistributedServer();
        
        //获取ZK连接
        server.getConnect();
        
        //利用ZK连接注册服务器信息
        server.registerServer(args[0]);
        
        //启动业务功能
        server.handleBussiness(args[0]);
    }
}


/**
 * Revision history
 * -------------------------------------------------------------------------
 * 
 * Date Author Note
 * -------------------------------------------------------------------------
 * 2018年5月26日 王弘博 create
 */

我们来测试一下服务端代码是否正确:

Run As --> Run Configurations --> Arguments --> 输入maple1(我的一台服务器的主机名)


观察控制台输出情况


看到我们打印的日志:maple1 is online.... /servers/server0000000004,正常运行

再重复上面操作,运行maple2,观察控制台

因为我在handleBussiness()方法里加了 Thread.sleep(Long.MAX_VALUE),

让我们程序一直睡眠下去方便我们来测试,可以点开控制台里的这个按钮来看一下,我们刚跑的两个程序都在运行中...

我们也可以打开终端里看一下是否创建了两个服务


我们可以发现,在servers下我们注册了两个服务,经测试,我们的服务端代码正确,

关闭着两个进程,开始写客户端代码

/*
 * Project: blog.web
 * 
 * File Created at 2018年5月26日
 * 
 * Copyright 2018 CMCC Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * ZYHY Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license.
 */
package com.maple.blog.web.zk;

import java.util.ArrayList;
import java.util.List;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

/**
 * @Type Client.java
 * @Desc 
 * @author 王弘博
 * @date 2018年5月26日 下午8:54:01
 * @version 
 */
public class DistributedClient {

    private static final String connectString = "192.168.116.128:2181,192.168.116.129:2181,192.168.116.130:2181";
    private static final int sessionTimeout = 2000;
    private static final String parentNode = "/servers";

    private volatile List<String> serverList;
    ZooKeeper zkClient = null;
    

    /**
     * 获取到ZK客户端的连接
     * @throws Exception
     */
    public void getConnect() throws Exception {
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                //收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
                try {
                    getServerList();
                } catch (Exception e) {
                }
            }
        });
    }
    
    public void getServerList() throws Exception {
        //获取服务器子节点信息,并对父节点监听
        List<String> children = zkClient.getChildren(parentNode, true);
        //先创建一个局部的list来存服务器信息
        List<String> servers = new ArrayList<>();
        for(String child : children) {
            // child 只是子节点的节点名,还要拼上父节点
            byte[] data = zkClient.getData(parentNode+"/"+child, false, null);
            servers.add(new String(data,"UTF-8"));//把服务器名称maple1 ,maple2等 放进servers里
        }
        //把servers赋值给成员变量serverList,以提供给各业务线程使用
        serverList = servers;
        //打印服务器列表
        System.out.println(serverList);
    }
    
    /**
     * 业务功能
     * @throws Exception 
     */
    public void handleBussiness() throws Exception {
        System.out.println("client start working.........");
        Thread.sleep(Long.MAX_VALUE);
    }
    
    
    public static void main(String[] args) throws Exception {
        
        DistributedClient client = new DistributedClient();
        
        //获取ZK连接
        client.getConnect();
        
        //获取servers的子节点(并监听),从中获取服务器信息列表
        client.getServerList();
        
        //启动业务功能
        client.handleBussiness();
    }
}


/**
 * Revision history
 * -------------------------------------------------------------------------
 * 
 * Date Author Note
 * -------------------------------------------------------------------------
 * 2018年5月26日 王弘博 create
 */

先来测试一下 Run As

因为我们服务端没有运行,所以这里打印的是空,不过着也表示我们的代码是正确的。

下面就来系统的测试一下吧。

选中你的工程,右键Export,java --> Runnable JAR file

分别把这个两个类打成jar包,server.jar 和 client.jar,这里我只展示打server.jar了。

做完以上操作,进到文件里找到我们打的两个jar包,在当前目录中打开cmd

我们先来启动maple1,输入 java -jar server.jar maple1

再启动maple2,重新打开一个新的黑窗口,输入 java -jar server.jar maple2


这样我们的服务就注册完成了,我们启动客户端看看服务注册成功没

再打开一个新的黑窗口,输入  java -jar client.jar


可以看到我们在客户端代码里打印的 服务器列表,被打印出来了,为 [maple2, maple1]

看来我们的代码都是正确的,那我们来测试下,如果我们服务端,有一台服务器maple1挂了,那我们的ZK能动态感知到吗?

我们把 maple1 的那一个黑窗口关闭,再观察客户端client的黑窗口的变化

2s之后会变化,因为我们代码里配的 private static final int sessionTimeout = 2000

上面的图一目了然,客户端能监听到服务器节点的运行状态,依赖于我们客户端代码里的这个

好了,就到这里了,我也是正在学习ZK的小白,博客里只是做一下笔记,分享一下所学,如能帮助各位实属本人荣幸,

如有错误烦请各位指正,多谢!^-^

猜你喜欢

转载自blog.csdn.net/qq_33101675/article/details/80469415