RPC系列之基於ZooKeeper實現服務註冊中心

最近在看與RPC相關的東西,在GitHub上看到一個使用Java實現的簡單RPC框架,因而本身也想用Java實現一個簡單的RPC,以便加深對於RPC框架的理解。本篇文章主要是記錄如何使用ZooKeeper做爲RPC框架的註冊中心,實現服務的註冊和發現。java

什麼是RPC?

RPC,即 Remote Procedure Call(遠程過程調用),說得通俗一點就是:調用遠程計算機上的服務,就像調用本地服務同樣。正式的描述是:一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。node

基於ZooKeeper實現的服務註冊中心

若是對於dubbo這款國產RPC框架有必定的瞭解,就知道最開始它是基於ZooKeeper實現服務的註冊和發現的。關於服務的註冊和發現,主要是把服務名以及服務相關的服務器IP地址註冊到註冊中心,在使用服務的時候,只須要根據服務名,就能夠獲得全部服務地址IP,而後根據必定的負載均衡策略來選擇IP地址。git

下圖是服務的註冊和發現接口:github

服務的註冊

在ZooKeeper的節點概念中,Znode有四種類型,PERSISTENT(持久節點)、PERSISTENT_SEQUENTIAL(持久的連續節點)、EPHEMERAL(臨時節點)、EPHEMERAL_SEQUENTIAL(臨時的連續節點)。Znode的類型在建立時肯定而且以後不能再修改。bash

關於服務的註冊,其實就是把服務和IP註冊到ZooKeeper的節點中。服務器

private ZkClient zkClient;

    public ZooKeeperServiceRegistry(String zkAddress) {
        // 建立 ZooKeeper 客戶端
        zkClient = new ZkClient(zkAddress, ZkConstants.SESSION_TIMEOUT, ZkConstants.CONNECTION_TIMEOUT);
        log.info("connect zookeeper");
    }

    @Override
    public void register(String serviceName, String serviceAddress) {

        try {
            String registryPath = ZkConstants.REGISTRY_PATH;
            if (!zkClient.exists(registryPath)) {
                zkClient.createPersistent(registryPath);
                log.info("zk create registry node: {}", registryPath);
            }
            //建立服務節點(持久化)
            String servicePath = registryPath + "/" + serviceName;
            if (!zkClient.exists(servicePath)) {
                zkClient.createPersistent(servicePath);
                log.info("zk create service node: {}", servicePath);
            }
            //建立 address 節點(臨時)
            String addressPath = servicePath + "/address-";
            String addressNode = zkClient.createEphemeralSequential(addressPath, serviceAddress);
            log.info("zk create ip address node: {}",addressNode);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("zk create error: {}", e.getMessage());
        }

    }
複製代碼

服務的發現

經過ZooKeeper的節點把服務名和IP寫入其節點中,這樣就實現了最簡單的服務註冊,下面來看下服務的發現。網絡

服務的發現就是根據服務名來獲取ZooKeeper節點中的IP地址。負載均衡

private String zkAddress;
    public ZooKeeperServiceDiscovery(String zkAddress) {
        this.zkAddress = zkAddress;
    }


    @Override
    public String discover(String serviceName) {
        ZkClient zkClient = new ZkClient(zkAddress, ZkConstants.SESSION_TIMEOUT, ZkConstants.CONNECTION_TIMEOUT);
        log.info("connect zookeeper....");
        try {
            String servicePath = ZkConstants.REGISTRY_PATH + "/" + serviceName;
            if (!zkClient.exists(servicePath)) {
                throw new SystemException(String.format("can not find any service node on path: %s", servicePath));
            }
            //獲取路徑的子節點
            List<String> addressList = zkClient.getChildren(servicePath);
            if (CollectionUtils.isEmpty(addressList)) {
                throw new SystemException(String.format("can not find any address node on path: %s", servicePath));
            }
            //獲取 address 節點
            String address;
            if (Objects.equals(addressList.size(), 1)) {
                //若是隻有一個地址,則獲取地址
                address = addressList.get(0);
                log.info("get only address node: {}", address);
            } else {
                //若是有多個ip,隨機選擇一個
                address = addressList.get(ThreadLocalRandom.current().nextInt(addressList.size()));
                log.info("get random address node:{}", address);
            }
            //獲取 address 節點的值
            String addressPath = servicePath + "/" + address;
            return zkClient.readData(addressPath);
        } finally {
            zkClient.close();
        }
    }
複製代碼

總結

經過測試樣例,實現了最簡單的服務註冊和發現功能。框架

public static void main(String[] args) {
        ServiceRegistry registry = new ZooKeeperServiceRegistry("127.0.0.1:2181");
        registry.register("rpc", "192.168.20.49:8080");
        ServiceDiscovery discovery = new ZooKeeperServiceDiscovery("127.0.0.1:2181");
        String address = discovery.discover("rpc");
        System.out.println("服務RPC的地址是:" + address);
    }
複製代碼

輸出:dom

服務RPC的地址是:192.168.20.49:8080
複製代碼

參考

相關文章
相關標籤/搜索