zookeeper实现分布式应用服务器上下线动态感知程序

问题描述:服务器是一个集群,客户端可以访问任意一个服务器进行交互,但是假如服务器集群中有一台机器下线,此时若客户端不能感知到服务器的上下线情况,则有可能会向下线的那台服务器发送请求,这样就无法访问服务器。

思路:借助zookeeper监听服务器上下行动态感知。zookeeper不用关心服务器集群的业务功能,只需要监听服务器集群的上下线即可。

解决方法

1.服务端启动时立即注册信息

服务器一启动就先在zookeeper中注册信息,这样zookeeper就会记录所有的服务器信息。但是要注意注册的数据节点必须是短暂节点。因为只有短暂节点在挂掉的时候,zookeeper会自动删除该节点,这样才能产生一个监听事件。可以将其设置成序列化短暂节点。可以保证每个节点都是唯一的。

2.对于客户端

a.一启动就先在zookeeper中getChildren,获取当前的在线服务器列表。这时请求哪一台就根据自己的策略请求。因为下线的服务器已经被剔除列表了,所以不会访问到下线服务器。

b.在geChildren时注册一个监听。这时客户端就可以监听到节点的变化(因为上面提到是短暂节点),就可以感知到上下线的服务器。在感知到信息后,客户端的process就可以做重新获取服务器列表并注册监听(因为监听只能监听到第一次事件变化,所以需要再次注册监听)的处理。

 

代码实现:(亲测有效,如果出错,可能是你的zookeeper集群没有开启或是其他不可抗因素!毕竟这种集群每个人都会遇到不同的问题!)

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.14</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/jline/jline -->
        <dependency>
            <groupId>jline</groupId>
            <artifactId>jline</artifactId>
            <version>0.9.94</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>


    </dependencies>

    <groupId>com.sora</groupId>
    <artifactId>com.sora</artifactId>
    <version>1.0-SNAPSHOT</version>


</project>

1.服务器端:

package zkdist;

import org.apache.zookeeper.*;

import java.util.concurrent.CountDownLatch;

/**
 * 分布式应用系统上下线动态感知服务端
 * **/
public class DistributedSysServer {
    private ZooKeeper zk = null;
    private static final String connectString="192.168.159.128:2181,192.168.159.130:2181,192.168.159.131:2181";
    private static final int sessionTimeout = 10000;
    private static final String parentNode = "/servers";
    //创建zk客户端连接
    public void getConnect() throws Exception{
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {
                if (Watcher.Event.KeeperState.SyncConnected.equals(watchedEvent.getState())){
                    System.out.println("连接成功" + watchedEvent);
                    countDownLatch.countDown();
                }
                try {
                    zk.getChildren("/", true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        if (ZooKeeper.States.CONNECTING.equals(zk.getState())){
            System.out.println("连接中");
            countDownLatch.await();
        }

    }
    //向zk集群注册服务器信息
    public void registServer(String hostname) throws Exception {
        String create = zk.create(parentNode + "/server", hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname+" is online..."+create);
    }

    //业务功能
    public void handleBusiness(String hostname) throws Exception{
        System.out.println(hostname + " start working...");
        Thread.sleep(Long.MAX_VALUE);//线程睡眠,可以一直监听(测试)
    }

    public static void main(String[] args) throws Exception{
        //获取zk连接
        DistributedSysServer server = new DistributedSysServer();
        server.getConnect();

        //利用zk连接注册服务器信息
        server.registServer(args[0]);

        //启动业务功能,此系统只要能感知服务器上下线即可,因此这里不用关心,这里用一个通用的方法模拟
        server.handleBusiness(args[0]);

    }



}

客户端:

package zkdist;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * 分布式系统服务器上线动态感知客户端
 * **/

public class DistributedSysClient {
    private ZooKeeper zk = null;
    private static final String connectString="192.168.159.128:2181,192.168.159.130:2181,192.168.159.131:2181";
    private static final int sessionTimeout = 10000;
    private static final String parentNode = "/servers";
    //注意要加volatile
    private volatile List<String> serverList;
    //创建zk客户端连接
    public void getConnect() throws Exception{
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {
                if (Watcher.Event.KeeperState.SyncConnected.equals(watchedEvent.getState())){
                    System.out.println("连接成功" + watchedEvent);
                    countDownLatch.countDown();
                }
                try {
                    //重新更新服务器列表并且注册了监听
                    getServerList();
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        if (ZooKeeper.States.CONNECTING.equals(zk.getState())){
            System.out.println("连接中");
            countDownLatch.await();
        }

    }

    //获取服务器列表并监听
    public void getServerList() throws KeeperException, InterruptedException {
        //获取服务器子节点信息,并且对父节点进行监听(监听子节点的上下线)
        List<String> children = zk.getChildren(parentNode, true);
        //先创建一个局部的list存服务器信息
        ArrayList<String> servers = new ArrayList<String>();
        //子节点数据变化不需要监听
        for(String child:children){
            byte[] data = zk.getData(parentNode + "/" + child, false, null);
            servers.add(new String(data));
        }
        //把servers赋值给volatile成员变量serverList,以提供给各业务线程使用
        serverList = servers;

        //打印服务器列表信息
        System.out.println(serverList.toString());
    }

    //业务功能
    public void handleBusiness() throws Exception{
        System.out.println("client start working ");
        Thread.sleep(Long.MAX_VALUE);//线程睡眠,可以一直监听(测试)
    }



    public static void main(String[] args) throws Exception {
        /**
         * 逻辑:
         * 客户机启动后先连接zk,然后获取服务列表,业务线程启动等待(线程睡眠,持续监听),
         * 当监听到事件时(节点发生变化),process会被调用(因为在获取服务列表的时候对父节点注册了监听),
         * process再次调用获取服务列表接口,就做到了重新更新列表并监听,
         * 执行业务逻辑......(循环下去)
         * **/

        //获取zk连接
        DistributedSysClient client = new DistributedSysClient();
        client.getConnect();

        //获取servers的子节点信息并监听,c从中获取服务器信息列表
        client.getServerList();

        //业务功能启动
        client.handleBusiness();

    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章