1. 程式人生 > >利用zookeeper實現自己的服務註冊中心

利用zookeeper實現自己的服務註冊中心

首先確定服務註冊中心的結構資訊:具體如下圖所示

首先定義客戶端註冊介面,定義了一些基本方法;

package lin.remoting.framework.register;

import java.util.List;
import java.util.Map;

/**
 * 消費端註冊中心
 */
public interface IRegisterCenterInvoker {

    /**
     * 由客戶端啟動首次從zookeeper中首次拉去資訊
     */
    public void initProviderMap();

    /**
     * 消費端獲取服務提供者資訊
     * @return
     */
    public Map<String,List<ProviderService>> getServiceMetaDataMap4Consume();

    public void registerInvoker(final InvokerService invoker);

}

具體實現類如下:

package lin.remoting.framework.register;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服務呼叫者註冊中心
 */
public class RegisterCenterInvoker implements IRegisterCenterInvoker {
    //服務提供者列表:key :服務提供者介面,value:服務提供者服務方法列表
    private static final Map<String, List<ProviderService>> providerServiceMap
            = new ConcurrentHashMap<String, List<ProviderService>>();
    public static final Map<String, List<ProviderService>> serviceMetaDataMapConsume
            = new ConcurrentHashMap<String, List<ProviderService>>();
    private static volatile ZkClient zkClient = null;

    private static String ZK_SERVICE = "127.0.0.1";
    private static int ZK_SESSION_TIME_OUT = 999999;
    private static int ZK_CONNECTION_TIME_OUT = 999999;

    private static RegisterCenterInvoker RegisterCenterInvoker = new RegisterCenterInvoker();

    private static String ROOT_PATH = "/config_register/" + "lin";
    private static String PROVIDER_TYPE = "/provider";
    private static String INVOKER_TYPE = "/consumer";

    private RegisterCenterInvoker() {
    }

    public static RegisterCenterInvoker singleton() {
        return RegisterCenterInvoker;
    }

    public void initProviderMap() {
        if (serviceMetaDataMapConsume.isEmpty()) {
            serviceMetaDataMapConsume.putAll(fetchOrUpdateServiceMetaData());
        }
    }

    /**
     * 使用該函式前請確保已經使用initProviderMap()已近從zookeeper拉取訊息到本地進行快取
     *
     * @return
     */
    public Map<String, List<ProviderService>> getServiceMetaDataMap4Consume() {
        return serviceMetaDataMapConsume;
    }

    /**
     * 該函式用於消費者將自身資訊註冊到zookeeper上
     *
     * @param invoker
     */
    public void registerInvoker(InvokerService invoker) {
        if (invoker == null) {
            return;
        }
        synchronized (RegisterCenterInvoker.class) {
            if (zkClient == null) {
                zkClient = new ZkClient(ZK_SERVICE, ZK_SESSION_TIME_OUT, ZK_CONNECTION_TIME_OUT, new SerializableSerializer());
            }
            boolean exist = zkClient.exists(ROOT_PATH);
            if (!exist) {
                zkClient.createPersistent(ROOT_PATH, true);
            }

            //建立服務消費者節點
            String consumeName = invoker.getTargetInterface().getSimpleName();
            exist = zkClient.exists(ROOT_PATH+"/"+consumeName);
            if (!exist) {
                zkClient.createPersistent(ROOT_PATH+"/"+consumeName);
            }
            String consumeNodePath = ROOT_PATH + "/" + consumeName + INVOKER_TYPE;
            exist = zkClient.exists(consumeNodePath);
            if (!exist) {
                zkClient.createPersistent(consumeNodePath);
            }
            //建立當前服務端節點
            String localIp = invoker.getIP();
            String currentConsumeServicePath = consumeNodePath + "/" + localIp;
            exist = zkClient.exists(currentConsumeServicePath);
            if (!exist) {
                zkClient.createEphemeral(currentConsumeServicePath);
            }


        }


    }

    private Map<String, List<ProviderService>> fetchOrUpdateServiceMetaData() {

        final Map<String, List<ProviderService>> providerServiceMap = new ConcurrentHashMap<String, List<ProviderService>>();
        List<ProviderService> providerServices = null;
        //連線zk 加鎖防止重複註冊。
        synchronized (IRegisterCenterInvoker.class) {
            if (zkClient == null) {
                zkClient = new ZkClient(ZK_SERVICE, ZK_SESSION_TIME_OUT,
                        ZK_CONNECTION_TIME_OUT, new SerializableSerializer());
            }
            //從這裡開始從伺服器獲取服務提供者列表
            String providerPath = ROOT_PATH;
            //獲取根節點下所有的子節點
            List<String> provideServices = zkClient.getChildren(providerPath);
            for (String serviceName : provideServices) {
                //指定服務名下的所有提供者路勁
                String servicePath = ROOT_PATH + "/" + serviceName + PROVIDER_TYPE;
                //所有提供者ip
                List<String> ipPathList = zkClient.getChildren(servicePath);
                for (String ipPath : ipPathList) {
                    String[] ipAndPort = ipPath.split("\\|");
                    String serverIp = ipAndPort[0];
                    String serverPort = ipAndPort[1];
                    //引用型 與初始創的為同一個引用
                    providerServices = providerServiceMap.get(serviceName);
                    if (providerServices == null) {
                        providerServices = new ArrayList<ProviderService>();
                        providerServiceMap.put(serviceName, providerServices);
                    }
                    ProviderService providerService = new ProviderService();
                    try {
                        //將服務名轉化為類  這裡根據自己的實際情況設定,事實上這裡可以通過配置
                        //檔案來確定
                        providerService.setTargetInterface(Class.forName("lin.remoting.framework.register." + serviceName));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    providerService.setIP(serverIp);
                    providerService.setPort(Integer.parseInt(serverPort));
                    //將服務新增到列表當中
                    providerServices.add(providerService);


                }
                //遍歷完後將服務列表新增到其中
                providerServiceMap.put(serviceName, providerServices);
                zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
                    public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                        if (currentChilds == null) {
                            currentChilds = new ArrayList<String>();
                        }
                        List<String> tmp = new ArrayList<String>();
                        for (String ipPort : currentChilds) {
                            tmp.add(ipPort.split("\\|")[0]);
                        }
                        //  System.out.println("父路徑"+parentPath);
                        //呼叫函式重新重新整理本地伺服器資料
                        refreshServiceMetaDataMap(tmp);

                    }
                });

            }


        }
        return providerServiceMap;
    }

    //  根據ip來判斷 一個服務提供者是否失去作用
    public void refreshServiceMetaDataMap(List<String> ipList) {
        if (ipList == null) {
            ipList = new ArrayList<String>();
        }
        Map<String, List<ProviderService>> currentServiceMetaDataMap = new ConcurrentHashMap<String, List<ProviderService>>();
        for (Map.Entry<String, List<ProviderService>> entry : serviceMetaDataMapConsume.entrySet()) {
            String serviceName = entry.getKey();
            List<ProviderService> providerServices = entry.getValue();

            List<ProviderService> currentProviders = currentServiceMetaDataMap.get(serviceName);
            if (currentProviders == null) {
                currentProviders = new ArrayList<ProviderService>();
            }
            //需要全部遍歷,因為一臺機器可能提供多個服務
            for (ProviderService providerService : providerServices) {
                if (ipList.contains(providerService.getIP())) {
                    currentProviders.add(providerService);
                }
            }
            currentServiceMetaDataMap.put(serviceName, currentProviders);
        }

        //hashMap 函式此時每個對用的serviceName已經對用當前的新服務提供者列表
        //服務名相同,當前currentProviders會覆蓋原鍵對應得值
        serviceMetaDataMapConsume.putAll(currentServiceMetaDataMap);
       /* Set<String> keys = serviceMetaDataMapConsume.keySet();

        for (String key : keys) {
            List<ProviderService> providerServices = RegisterCenterInvoker.serviceMetaDataMapConsume.get(key);

          //  System.out.println("跟新大小" + providerServices.size() + "size");
            for (ProviderService providerService : providerServices) {

                System.out.println(providerService);
            }

        }

*/
    }
}

下面是上面所用到的一些類

package lin.remoting.framework.register;

/**
 * 用於封裝一個服務消費者的基本資訊
 */
public class InvokerService {
    private Class<?> targetInterface;

    private String IP;

    public InvokerService(Class<?> targetInterface, String IP) {
        this.targetInterface = targetInterface;
        this.IP = IP;

    }

    public Class<?> getTargetInterface() {
        return targetInterface;
    }

    public void setTargetInterface(Class<?> targetInterface) {
        this.targetInterface = targetInterface;
    }

    public String getIP() {
        return IP;
    }

    public void setIP(String IP) {
        this.IP = IP;
    }


}
package lin.remoting.framework.register;

//該類用於封裝提供服務類訊息
public class ProviderService {
    //服務介面 即該服務提供者面向的物件
    private Class<?> targetInterface;
    //服務提供者地址
    private String IP;
    //服務提供者的埠號;
    private int port;

    public Class<?> getTargetInterface() {
        return targetInterface;
    }

    public void setTargetInterface(Class<?> targetInterface) {
        this.targetInterface = targetInterface;
    }

    public String getIP() {
        return IP;
    }

    public void setIP(String IP) {
        this.IP = IP;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return this.targetInterface.getName() + " IP: " + this.IP + " port: " + this.port;
    }
}
package lin.remoting.framework.register;

public class Myservice {
}

關於伺服器端的註冊中心的實現將在我的下一篇博文為大家實現

相關推薦

利用zookeeper實現自己服務註冊中心

首先確定服務註冊中心的結構資訊:具體如下圖所示 首先定義客戶端註冊介面,定義了一些基本方法; package lin.remoting.framework.register; import java.util.List; import java.util.Map;

利用Zookeeper實現分布式鎖及服務註冊中心

建立連接 oid 等待 某個文件 改名 解壓 als 中文字符 blog 原文:利用Zookeeper實現分布式鎖及服務註冊中心對於Zookeeper的定義以及原理,網上已經有很多的優秀文章對其進行了詳細的介紹,所以本文不再進行這方面的闡述。 本文主要介紹一些基本的準備工

使用zookeeper作為服務註冊中心

zookeeper作為服務註冊中心 下載安裝 zookeeper官網下載最新版,修改conf/zoo.cfg配置檔案,如果是windows系統必須指定dataDir具體路徑 執行bin/zkServer.cmd啟動zookeeper服務 注意,zookeeper版本必

SpringCloud服務註冊中心比較:Consul vs Zookeeper vs Etcd vs Eureka

這裡就平時經常用到的服務發現的產品進行下特性的對比,首先看下結論: Feature Consul zookeeper etcd euerka 服務健康檢查 服務狀態,記憶體,硬碟等 (弱)長連線,keepalive 連線心跳 可配支援 多資料中心

Spring Cloud 中使用zookeeper作為服務註冊中心與配置中心

前段時間,瞭解了通過spring-cloud-config-server與spring-cloud-eureka-server作為配置中心與註冊中心,同時瞭解到基於zookeeper或consul可以完成同樣的事情,所以必須瞭解一下,這樣有利於實際工作的技術對比

服務註冊中心Eureka vs Zookeeper vs Consul

初始 fan 協調 ip協議 恢復模式 出錯 維護 機器 更新數據 前言    在現在雲計算和大數據快速發展的今天,業務快速發展和變化。我們以前的單一應用難以應對這種快速的變化, 因此我們需要將以前單一的大應用不斷進行差分,分成若幹微小的應用或者服務,這就是微服務的思想。但

Spring Cloud使用Consul或Zookeeper替換Euraka作為服務註冊中心

一、使用Consul替換Euraka作為服務註冊中心 1、“服務提供者”啟動類註解替換 將註解@EnableEurekaClient替換成@EnableDiscoveryClient 2、pom.xml檔案中新增依賴 <dependency>

Spring Cloud+ spring boot +mybatis+ mysql+ Eureka+ Ribbon實現 服務註冊中心 服務提供 服務消費

一、spring cloud簡介spring cloud 為開發人員提供了快速構建分散式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理、事件匯流排、全域性鎖、決策競選、分散式會話等等。它執行環境簡單,可以在開發人員的電腦上跑。另外說明spring cloud是基

基於ZooKeeper服務註冊中心

本文介紹基於ZooKeeper的Dubbo服務註冊中心的原理。 1.ZooKeeper中的節點 ZooKeeper是一個樹形結構的目錄服務,支援變更推送,因此非常適合作為Dubbo服務的註冊中心。 注:在ZooKeeper中,節點分為兩類,第一類

SpringCloud 筆記 (一)---- 簡單搭建服務註冊中心服務實現服務註冊中心高可用

此spring cloud筆記系列都來源於翟永超的spring cloud微服務實戰一書,可自行去下載,我這裡只是記錄一下學習的筆記 此文件有關於服務註冊中心。 快速構建一個服務註冊中心專案 Pom <parent>

服務註冊中心,Eureka比Zookeeper好在哪裡?

開發十年,就只剩下這套架構體系了! >>>   

螞蟻金服服務註冊中心 MetaServer 功能介紹和實現剖析 | SOFARegistry 解析

SOFAStack (Scalable Open Financial  Architecture Stack) 是螞蟻金服自主研

Spring Cloud Eureka 分散式開發之服務註冊中心、負載均衡、宣告式服務呼叫實現

介紹 本示例主要介紹 Spring Cloud 系列中的 Eureka,使你能快速上手負載均衡、宣告式服務、服務註冊中心等 Eureka Server Eureka 是 Netflix 的子模組,它是一個基於 REST 的服務,用於定位服務,以實現雲端中間層服務發現和故障轉移。 服務註冊和發現對於微服務架

學習一下 SpringCloud (二)-- 服務註冊中心 Eureka、Zookeeper、Consul、Nacos

(1) 相關博文地址: 學習一下 SpringCloud (一)-- 從單體架構到微服務架構、程式碼拆分(maven 聚合): https://www.cnblogs.com/l-y-h/p/14105682.html (2)程式碼地址: https://github.com/lyh-man/Sprin

使用Spring Cloud搭建服務註冊中心

gets log 筆記本電腦 這樣的 ins 閱讀 筆記本 快速 名稱 我們在之前的博客中已經介紹過阿裏的分布式服務框架dubbo【Linux上安裝Zookeeper以及一些註意事項】【一個簡單的案例帶你入門Dubbo分布式框架】,但是小夥伴們應該也看到了,阿裏的dubbo

springboot 註冊服務註冊中心(zk)的兩種方式

stop height span implement exceptio com strong class sta 在使用springboot進行開發的過程中,我們經常需要處理這樣的場景:在服務啟動的時候,需要向服務註冊中心(例如zk)註冊服務狀態,以便當服務狀態改變的時

Spring Cloud Eureka 2 (Eureka Server搭建服務註冊中心)

class XML bsp gist client intellij 嘗試 ati register 工具:IntelliJ IDEA 2017.1.2 x64、maven3.3.9 打開IDE file===>new===>project next

Spring Cloud Eureka 4 (高可用服務註冊中心)

自己 def port hello -c 圖片 sys img 效果 在微服務這樣的分布式環境中,我們需要充分考慮發生故障的情況,所以在生產環境中必須考慮對各個組件進行高可用部署,對於服務註冊中心也是一樣。 Eureka Server 的高可用實際上就是講自己作為服

從零開始搭建系統3.2——微服務註冊中心開發及部署

註冊 cnblogs 開始 htm www post 服務註冊 logs get 從零開始搭建系統3.2——微服務註冊中心開發及部署從零開始搭建系統3.2——微服務註冊中心開發及部署

Spring Cloud構建微服務架構—創建“服務註冊中心

springboot springcloud mybatis eureka config 創建一個基礎的Spring Boot工程,命名為eureka-server,並在pom.xml中引入需要的依賴內容: <parent> <groupId>org.springf