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();

    }

}

 

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