SpringBoot2 整合 Zookeeper組件,管理架構中服務協調

本文源碼:GitHub·點這裏 || GitEE·點這裏java

1、Zookeeper基礎簡介

一、概念簡介

Zookeeper是一個Apache開源的分佈式的應用,爲系統架構提供協調服務。從設計模式角度來審視:該組件是一個基於觀察者模式設計的框架,負責存儲和管理數據,接受觀察者的註冊,一旦數據的狀態發生變化,Zookeeper就將負責通知已經在Zookeeper上註冊的觀察者作出相應的反應,從而實現集羣中相似Master/Slave管理模式。ZooKeeper的目標就是封裝好複雜易出錯的關鍵服務,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。node

二、基本理論

  • 數據結構

ZooKeeper記錄數據的結構與Linux文件系統類似,總體能夠看做一棵樹,每一個節點稱ZNode。每一個Znode默認可以存儲1MB的數據,每一個ZNode均可以經過其路徑惟一標識。git

zk01.png

  • 節點類型

短暫(ephemeral):客戶端和服務器端斷開鏈接後,建立的節點自動刪除。
持久(persistent):客戶端和服務器端斷開鏈接後,建立的節點持久化保存。github

  • 集羣服務

在Zookeeper集羣服務是由一個領導者(leader),多個跟隨者(follower)組成的集羣。領導者負責進行投票的發起和決議,更新集羣服務狀態。跟隨者用於接收客戶請求並向客戶端返回結果,在選舉Leader過程當中參與投票。集羣中只要有半數以上節點存活,Zookeeper集羣就能正常服務。apache

  • 數據一致性

每一個server保存一份相同的數據拷貝,客戶端不管請求到被集羣中哪一個server處理,獲得的數據都是一致的。segmentfault

三、應用場景

  • 經典應用:Dubbo框架的服務註冊和發現;
  • 分佈式消息同步和協調機制;
  • 服務器節點動態上下線;
  • 統一配置管理、負載均衡、集羣管理;

2、安全管理操做

一、操做權限

ZooKeeper的節點有5種操做權限:CREATE(增)、READ(查)、WRITE(改)、DELETE(刪)、ADMIN(管理)等相關權限,這5種權限集合能夠簡寫爲crwda,每一個單詞的首字符拼接而成。設計模式

二、認證方式:

  • world

默認方式,開放的權限,意解爲全世界都能隨意訪問。緩存

  • auth

已經受權且認證經過的用戶才能夠訪問。安全

  • digest

用戶名:密碼方式認證,實際業務開發中最經常使用的方式。服務器

  • IP白名單

受權指定的Ip地址,和指定的權限點,控制訪問。

三、Digest受權流程

  • 添加認證用戶

addauth digest 用戶名:密碼

  • 設置權限

setAcl /path auth:用戶名:密碼:權限

  • 查看Acl設置

getAcl /path

  • 完整操做流程
-- 添加受權用戶
[zk: localhost:2181] addauth digest smile:123456
-- 建立節點
[zk: localhost:2181] create /cicada cicada
-- 節點受權
[zk: localhost:2181] setAcl /cicada auth:smile:123456:cdrwa
-- 查看受權
[zk: localhost:2181] getAcl /cicada

3、整合 SpringBoot2 框架

一、核心依賴

Curator是Apache開源的一個Zookeeper客戶端鏈接和操做的組件,Curator框架在Zookeeper原生API接口上進行二次包裝。提供ZooKeeper各類應用場景:好比:分佈式鎖服務、集羣領導選舉、共享計數器、緩存機制、分佈式隊列等API封裝。

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-client</artifactId>
    <version>2.12.0</version>
</dependency>

二、Zookeeper參數

zoo:
  keeper:
    #開啓標誌
    enabled: true
    #服務器地址
    server: 127.0.0.1:2181
    #命名空間,被稱爲ZNode
    namespace: cicada
    #權限控制,加密
    digest: smile:123456
    #會話超時時間
    sessionTimeoutMs: 3000
    #鏈接超時時間
    connectionTimeoutMs: 60000
     #最大重試次數
    maxRetries: 2
    #初始休眠時間
    baseSleepTimeMs: 1000

三、服務初始化配置

@Configuration
public class ZookeeperConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperConfig.class) ;
    @Resource
    private ZookeeperParam zookeeperParam ;
    private static CuratorFramework client = null ;
    /**
     * 初始化
     */
    @PostConstruct
    public void init (){
        //重試策略,初試時間1秒,重試10次
        RetryPolicy policy = new ExponentialBackoffRetry(
                zookeeperParam.getBaseSleepTimeMs(),
                zookeeperParam.getMaxRetries());
        //經過工廠建立Curator
        client = CuratorFrameworkFactory.builder()
                .connectString(zookeeperParam.getServer())
                .authorization("digest",zookeeperParam.getDigest().getBytes())
                .connectionTimeoutMs(zookeeperParam.getConnectionTimeoutMs())
                .sessionTimeoutMs(zookeeperParam.getSessionTimeoutMs())
                .retryPolicy(policy).build();
        //開啓鏈接
        client.start();
        LOGGER.info("zookeeper 初始化完成...");
    }
    public static CuratorFramework getClient (){
        return client ;
    }
    public static void closeClient (){
        if (client != null){
            client.close();
        }
    }
}

四、封裝系列接口

public interface ZookeeperService {
    /**
     * 判斷節點是否存在
     */
    boolean isExistNode (final String path) ;
    /**
     * 建立節點
     */
    void createNode (CreateMode mode,String path ) ;
    /**
     * 設置節點數據
     */
    void setNodeData (String path, String nodeData) ;
    /**
     * 建立節點
     */
    void createNodeAndData (CreateMode mode, String path , String nodeData) ;
    /**
     * 獲取節點數據
     */
    String getNodeData (String path) ;
    /**
     * 獲取節點下數據
     */
    List<String> getNodeChild (String path) ;
    /**
     * 是否遞歸刪除節點
     */
    void deleteNode (String path,Boolean recursive) ;
    /**
     * 獲取讀寫鎖
     */
    InterProcessReadWriteLock getReadWriteLock (String path) ;
}

五、接口實現

@Service
public class ZookeeperServiceImpl implements ZookeeperService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperServiceImpl.class);
    @Override
    public boolean isExistNode(String path) {
        CuratorFramework client = ZookeeperConfig.getClient();
        client.sync() ;
        try {
            Stat stat = client.checkExists().forPath(path);
            return client.checkExists().forPath(path) != null;
        } catch (Exception e) {
            LOGGER.error("isExistNode error...", e);
            e.printStackTrace();
        }
        return false;
    }
    @Override
    public void createNode(CreateMode mode, String path) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        try {
            // 遞歸建立所需父節點
            client.create().creatingParentsIfNeeded().withMode(mode).forPath(path);
        } catch (Exception e) {
            LOGGER.error("createNode error...", e);
            e.printStackTrace();
        }
    }
    @Override
    public void setNodeData(String path, String nodeData) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        try {
            // 設置節點數據
            client.setData().forPath(path, nodeData.getBytes("UTF-8"));
        } catch (Exception e) {
            LOGGER.error("setNodeData error...", e);
            e.printStackTrace();
        }
    }
    @Override
    public void createNodeAndData(CreateMode mode, String path, String nodeData) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        try {
            // 建立節點,關聯數據
            client.create().creatingParentsIfNeeded().withMode(mode)
                  .forPath(path,nodeData.getBytes("UTF-8"));
        } catch (Exception e) {
            LOGGER.error("createNode error...", e);
            e.printStackTrace();
        }
    }
    @Override
    public String getNodeData(String path) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        try {
            // 數據讀取和轉換
            byte[] dataByte = client.getData().forPath(path) ;
            String data = new String(dataByte,"UTF-8") ;
            if (StringUtils.isNotEmpty(data)){
                return data ;
            }
        }catch (Exception e) {
            LOGGER.error("getNodeData error...", e);
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public List<String> getNodeChild(String path) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        List<String> nodeChildDataList = new ArrayList<>();
        try {
            // 節點下數據集
            nodeChildDataList = client.getChildren().forPath(path);
        } catch (Exception e) {
            LOGGER.error("getNodeChild error...", e);
            e.printStackTrace();
        }
        return nodeChildDataList;
    }
    @Override
    public void deleteNode(String path, Boolean recursive) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        try {
            if(recursive) {
                // 遞歸刪除節點
                client.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);
            } else {
                // 刪除單個節點
                client.delete().guaranteed().forPath(path);
            }
        } catch (Exception e) {
            LOGGER.error("deleteNode error...", e);
            e.printStackTrace();
        }
    }
    @Override
    public InterProcessReadWriteLock getReadWriteLock(String path) {
        CuratorFramework client = ZookeeperConfig.getClient() ;
        // 寫鎖互斥、讀寫互斥
        InterProcessReadWriteLock readWriteLock = new InterProcessReadWriteLock(client, path);
        return readWriteLock ;
    }
}

六、基於Swagger2接口

@Api("Zookeeper接口管理")
@RestController
public class ZookeeperApi {
    @Resource
    private ZookeeperService zookeeperService ;
    @ApiOperation(value="查詢節點數據")
    @GetMapping("/getNodeData")
    public String getNodeData (String path) {
        return zookeeperService.getNodeData(path) ;
    }
    @ApiOperation(value="判斷節點是否存在")
    @GetMapping("/isExistNode")
    public boolean isExistNode (final String path){
        return zookeeperService.isExistNode(path) ;
    }
    @ApiOperation(value="建立節點")
    @GetMapping("/createNode")
    public String createNode (CreateMode mode, String path ){
        zookeeperService.createNode(mode,path) ;
        return "success" ;
    }
    @ApiOperation(value="設置節點數據")
    @GetMapping("/setNodeData")
    public String setNodeData (String path, String nodeData) {
        zookeeperService.setNodeData(path,nodeData) ;
        return "success" ;
    }
    @ApiOperation(value="建立並設置節點數據")
    @GetMapping("/createNodeAndData")
    public String createNodeAndData (CreateMode mode, String path , String nodeData){
        zookeeperService.createNodeAndData(mode,path,nodeData) ;
        return "success" ;
    }
    @ApiOperation(value="遞歸獲取節點數據")
    @GetMapping("/getNodeChild")
    public List<String> getNodeChild (String path) {
        return zookeeperService.getNodeChild(path) ;
    }
    @ApiOperation(value="是否遞歸刪除節點")
    @GetMapping("/deleteNode")
    public String deleteNode (String path,Boolean recursive) {
        zookeeperService.deleteNode(path,recursive) ;
        return "success" ;
    }
}

4、源代碼地址

GitHub·地址
https://github.com/cicadasmile/middle-ware-parent
GitEE·地址
https://gitee.com/cicadasmile/middle-ware-parent

相關文章
相關標籤/搜索