Windows Zookeeper 單機(jī)模式和偽分布式模式安裝

目錄

zk目錄.png

Windows安裝zk版本為:zookeeper-3.4.14

  • 1、單節(jié)點(diǎn)方式:部署在一臺(tái)服務(wù)器上
  • 2、單IP多節(jié)點(diǎn)(偽集群):部署在同一IP,但是有多個(gè)節(jié)點(diǎn)谨敛,各有自己的端口
  • 3、多IP多節(jié)點(diǎn):部署在不同IP滤否,各有自己的端口(未測(cè)試)

一脸狸、Zookeeper安裝(單機(jī)部署)

  • 單節(jié)點(diǎn)方式:部署在一臺(tái)服務(wù)器上

1、下載地址:

鏈接:zookeeper-3.4.14.tar.gz 提取碼:l77v
下載完--解壓縮后的目錄:D:\zookeeper-3.4.14

zk解壓縮后目錄.png

2藐俺、添加tmp文件夾

添加tmp文件夾-用作存日志和數(shù)據(jù)炊甲,在目錄D:\zookeeper-3.4.14\下


tmp.png

并且在tmp下添加data和log文件夾


tmp2.png

3泥彤、打開D:\zookeeper-3.4.14\conf

把這個(gè)zoo_sample.cfg文件重命名為zoo.cfg


zoocfg.png

打開zoo.cfg : 注意最好不要把中文注釋復(fù)制進(jìn)去,容易出現(xiàn)閃退情況卿啡,下面有出現(xiàn)閃退的解決方式吟吝。

tickTime=2000
initLimit=10
syncLimit=5
# 配置數(shù)據(jù)存放地址,剛剛上面創(chuàng)建的文件夾
dataDir=D:\\zookeeper-3.4.14\\tmp\\data  
# 配置日志存放地址牵囤,剛剛上面創(chuàng)建的文件夾
dataLogDir=D:\\zookeeper-3.4.14\\tmp\\log   
# 默認(rèn)斷開2181 ,這里更改端口為2182
clientPort=2182   


ps:參數(shù)說明

  • tickTime:心跳間隔滞伟,這個(gè)時(shí)間作為zookeeper服務(wù)器之間或zookeeper服務(wù)器與客戶端服務(wù)器維持心跳的時(shí)間間隔揭鳞,即每隔 tickTime 時(shí)間就會(huì)發(fā)送一個(gè)心跳
  • initLimit:這個(gè)配置項(xiàng)是用來配置 Zookeeper 接受客戶端(這里所說的客戶端不是用戶連接 Zookeeper 服務(wù)器的客戶端,而是 Zookeeper 服務(wù)器集群中連接到 Leader 的 Follower 服務(wù)器)初始化連接時(shí)最長(zhǎng)能忍受多少個(gè)心跳時(shí)間間隔數(shù)梆奈。當(dāng)已經(jīng)超過 5個(gè)心跳的時(shí)間(也就是 tickTime)長(zhǎng)度后 Zookeeper 服務(wù)器還沒有收到客戶端的返回信息野崇,那么表明這個(gè)客戶端連接失敗∧吨樱總的時(shí)間長(zhǎng)度就是 52000=10 秒
  • syncLimit:這個(gè)配置項(xiàng)表示 Leader 與 Follower 之間發(fā)送消息乓梨,請(qǐng)求和相應(yīng)時(shí)間長(zhǎng)度,最長(zhǎng)不能超過多少個(gè) tickTime 的時(shí)間長(zhǎng)度清酥,總的時(shí)間長(zhǎng)度就是 22000=4 秒
  • dataDir:zookeeper存儲(chǔ)數(shù)據(jù)的目錄扶镀,默認(rèn)情況下,zookeeper的日志問價(jià)也會(huì)保存至該目錄
  • clientPort:客戶端連接zookeeper的端口號(hào)
  • server.A=B:C:D:其中 A 是一個(gè)整形數(shù)字焰轻,表示服務(wù)器下標(biāo)臭觉;B 是這個(gè)服務(wù)器的 ip 地址;C 表示的是這個(gè)服務(wù)器與集群中的 Leader 服務(wù)器交換信息的端口辱志;D 表示的是萬一集群中的 Leader 服務(wù)器掛了蝠筑,需要一個(gè)端口來重新進(jìn)行選舉,選出一個(gè)新的 Leader揩懒,而這個(gè)端口就是用來執(zhí)行選舉時(shí)服務(wù)器相互通信的端口什乙。如果是偽集群的配置方式,由于 B 都是一樣已球,所以不同的 Zookeeper 實(shí)例通信端口號(hào)不能一樣臣镣,所以要給它們分配不同的端口號(hào)。

4智亮、啟動(dòng)zookeeper

進(jìn)入D:\zookeeper-3.4.14\bin


zk啟動(dòng).png

雙擊zkServer.cmd退疫,也可以右擊zkServer.cmd-發(fā)送到-桌面快捷方式,以后可以在桌面啟動(dòng)


zk啟動(dòng)成功.png

啟動(dòng)成功標(biāo)識(shí):binding to port 0.0.0.0/0.0.0.0:2182

cmd命令窗口閃退鸽素,這時(shí)可以修改zkServer.cmd文件內(nèi)容褒繁,在末尾加上 pause關(guān)鍵字,之后再啟動(dòng)cmd窗口不會(huì)閃退馍忽,且可看到啟動(dòng)時(shí)所拋出的異常棒坏,可以根據(jù)異常描述進(jìn)行相關(guān)處理燕差,我的異常-注解中文亂碼

5、測(cè)試zookeeper是否可用:

5.1 maven項(xiàng)目的pom.xml中先添加以下依賴項(xiàng)

<!--zookeeper-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
</dependency>

5.2 最基本的示例程序

package com.dist.zk;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

/**
 * @author zhengja@dist.com.cn
 * @data 2019/8/21 15:21
 */
public class ZooKeeperHello {

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        <!--更改成本機(jī)ip地址測(cè)試坝冕,進(jìn)入cmd 命令 ipconfig 查看IP-->
        ZooKeeper zk = new ZooKeeper("192.168.2.113:2182", 300000, new DemoWatcher());//連接zk server
        String node = "/app1";
        Stat stat = zk.exists(node, false);//檢測(cè)/app1是否存在
        if (stat == null) {
            //創(chuàng)建節(jié)點(diǎn)
            String createResult = zk.create(node, "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println(createResult);
        }
        //獲取節(jié)點(diǎn)的值
        byte[] b = zk.getData(node, false, stat);
        System.out.println(new String(b));
        zk.close();
    }

    static class DemoWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            System.out.println("----------->");
            System.out.println("path:" + event.getPath());
            System.out.println("type:" + event.getType());
            System.out.println("stat:" + event.getState());
            System.out.println("<-----------");
        }
    }

}

控制臺(tái)打印效果:

----------->
path:null
type:None
stat:SyncConnected
<-----------
15:24:45.503 [main-SendThread(192.168.2.113:2182)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply sessionid:0x100015c2c980002, packet:: clientPath:null serverPath:null finished:false header:: 1,3  replyHeader:: 1,21,0  request:: '/app1,T  response:: s{17,17,1566372182125,1566372182125,0,0,0,0,4,0,17} 
15:24:45.510 [main-SendThread(192.168.2.113:2182)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply sessionid:0x100015c2c980002, packet:: clientPath:null serverPath:null finished:false header:: 2,4  replyHeader:: 2,21,0  request:: '/app1,F  response:: #74657374,s{17,17,1566372182125,1566372182125,0,0,0,0,4,0,17} 
test

單機(jī)部署完成徒探,測(cè)試成功!
這個(gè)版本的zk默是支持遠(yuǎn)程連接的喂窟,不需要配置本機(jī)ip地址测暗,也能讓別人連接!

二磨澡、Zookeeper 偽分布式安裝(集群)

  • 單IP多節(jié)點(diǎn)(偽集群):部署在同一IP碗啄,但是有多個(gè)節(jié)點(diǎn),各有自己的端口稳摄。

  • 偽分布式安裝就是在同一臺(tái)pc上安裝稚字,安裝時(shí)使用同一個(gè)zookeeper包,多個(gè)配置文件分別配置為不同的端口.由于機(jī)器數(shù)量有限厦酬,這里采用偽分布式配置模擬集群配置胆描。

zk 版本:Windows安裝zk版本為:zookeeper-3.4.14
下載鏈接:zookeeper-3.4.14.tar.gz 提取碼:l77v

1、解壓縮后的目錄

下載完--解壓縮后的目錄:D:\zookeeper-3.4.14


zk解壓縮后目錄.png

2仗阅、修改配置

進(jìn)入conf目錄下把zoo_sample.cfg文件重名為:zoo.cfg昌讲,并修改配置為如下:注意最好不要把中文注釋復(fù)制進(jìn)去,容易出現(xiàn)閃退情況

tickTime=2000
initLimit=10
syncLimit=5
# 配置數(shù)據(jù)存放地址减噪,剛剛上面創(chuàng)建的文件夾
dataDir=D:\\zookeeper-3.4.14\\tmp\\data
# 配置日志存放地址剧蚣,剛剛上面創(chuàng)建的文件夾
dataLogDir=D:\\zookeeper-3.4.14\\tmp\\log 
# 默認(rèn)斷開2181 ,這里更改端口為2182
clientPort=2182   
添加tmp文件夾

添加tmp文件夾旋廷,在目錄D:\zookeeper-3.4.14\下:
D:\zookeeper-3.4.14\tmp


tmp.png

并且在tmp下添加data和log文件夾:D:\zookeeper-3.4.14\tmp\data鸠按、D:\zookeeper-3.4.14\tmp\log


tmp2.png

3、啟動(dòng)zookeeper服務(wù):

進(jìn)入目錄:D:\zookeeper-3.4.14\bin饶碘,雙擊執(zhí)行zkServer.cmd目尖,這樣就啟動(dòng)了zookeeper服務(wù)了
關(guān)閉zkServer窗口,zookeeper服務(wù)器也就關(guān)閉了

4扎运、偽分布式安裝

偽分布式安裝就是在同一臺(tái)pc上安裝瑟曲,安裝時(shí)使用同一個(gè)zookeeper包,多個(gè)配置文件分別配置為不同的端口豪治。我這里配置3個(gè)偽服務(wù)洞拨。

1.)將D:\zookeeper-3.4.14\conf\下的zoo.cfg分別復(fù)制出文件zoo1.cfg,zoo2.cfg,zoo3.cfg三個(gè)文件,并分別修改配置為:

zoo.png

分別修改配置為:
zoo1.cfg 注意最好不要把中文注釋復(fù)制進(jìn)去,容易出現(xiàn)閃退情況

tickTime=2000
initLimit=10
syncLimit=5

# 配置數(shù)據(jù)存放地址负拟,與下面創(chuàng)建的目錄一致
dataDir=D:\\zookeeper-3.4.14\\tmp\\data\\1     
#配置日志存放地址烦衣,與下面創(chuàng)建的目錄一致
dataLogDir=D:\\zookeeper-3.4.14\\tmp\\log\\1   
# 默認(rèn)斷開2181 ,這里更改端口為2182
clientPort=2182   

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889

zoo2.cfg

tickTime=2000
initLimit=10
syncLimit=5

dataDir=D:\\zookeeper-3.4.14\\tmp\\data\\2   
dataLogDir=D:\\zookeeper-3.4.14\\tmp\\log\\2
clientPort=2182 

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889

zoo3.cfg

tickTime=2000
initLimit=10
syncLimit=5

dataDir=D:\\zookeeper-3.4.14\\tmp\\data\\3    
dataLogDir=D:\\zookeeper-3.4.14\\tmp\\log\\3   
clientPort=2184   

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889


備注:
  假設(shè)把配置文件表示為:zoo{num}.cfg, server.{num}=ip/domain:Port1:Port2
  其中 num:表示數(shù)字表示第幾號(hào)服務(wù)器;ip/domain :是服務(wù)器域名或者ip地址花吟。
   Port1:表示這個(gè)服務(wù)器和集群中的Leader服務(wù)器交換信息的端口秸歧;
   Port2:表示萬一集群中的Leader服務(wù)器掛了,需要一個(gè)端口重新進(jìn)行選舉衅澈,選出一個(gè)新的Leader键菱,這個(gè)端口就是用來執(zhí)行選舉時(shí)服務(wù)器相互通信的端口。
? 由于我們是偽集群今布,所以ip或者域名是一樣的经备,所以要分配不同的端口號(hào)
? server.A=B:C:D:其中 A 是一個(gè)整形數(shù)字,表示服務(wù)器下標(biāo),與myid文件中的id是一致的部默;B 是這個(gè)服務(wù)器的 ip 地址侵蒙;C 表示的是這個(gè)服務(wù)器與集群中的 Leader 服務(wù)器交換信息的端口;D 表示的是萬一集群中的 Leader 服務(wù)器掛了甩牺,需要一個(gè)端口來重新進(jìn)行選舉蘑志,選出一個(gè)新的 Leader累奈,而這個(gè)端口就是用來執(zhí)行選舉時(shí)服務(wù)器相互通信的端口贬派。如果是偽集群的配置方式,由于 B 都是一樣澎媒,所以不同的 Zookeeper 實(shí)例通信端口號(hào)不能一樣搞乏,所以要給它們分配不同的端口號(hào)。


創(chuàng)建目錄

創(chuàng)建存數(shù)據(jù)data子目錄:
D:\zookeeper-3.4.14\tmp\data\1戒努、D:\zookeeper-3.4.14\tmp\data\2请敦、D:\zookeeper-3.4.14\tmp\data\3


偽集群data目錄.png

分別在三個(gè)文件下創(chuàng)建myid文件,文件內(nèi)容依次為:1储玫,2侍筛,3


myid.png

創(chuàng)建存日志log子目錄:
D:\zookeeper-3.4.14\tmp\log\1,D:\zookeeper-3.4.14\tmp\log\2撒穷、D:\zookeeper-3.4.14\tmp\log\3


偽集群log目錄.png

5匣椰、修改zkServer.cmd

進(jìn)入D:\zookeeper-3.4.14\bin下復(fù)制文件zkServer.cmd為zkServer-1.cmd,zkServer-2.cmd,zkServer-3.cmd


zkServer修改.png

5.1 修改zkServer-1.cmd 內(nèi)容修改為如下:

setlocal
call "%~dp0zkEnv.cmd"

set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain
set ZOOCFG=D:\\zookeeper-3.4.14\\conf\\zoo3.cfg
echo on
call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*

endlocal

5.2 修改zkServer-2.cmd 內(nèi)容修改為如下:

setlocal
call "%~dp0zkEnv.cmd"

set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain
REM 添加配置路徑 ZOOCFG
set ZOOCFG=D:\\zookeeper-3.4.14\\conf\\zoo3.cfg
echo on
call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*

endlocal

5.3 修改zkServer-3.cmd 內(nèi)容修改為如下:

setlocal
call "%~dp0zkEnv.cmd"

set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain
set ZOOCFG=D:\\zookeeper-3.4.14\\conf\\zoo3.cfg
echo on
call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*

endlocal

6、配置完成 啟動(dòng)zkServer

分別啟動(dòng)zkServer-1.cmd,zkServer-2.cmd,zkServer-3.cmd

進(jìn)入目錄:D:\zookeeper-3.4.14\bin端礼,分布執(zhí)行zkServer-1.cmd,zkServer-2.cmd,zkServer-3.cmd,啟動(dòng)偽分布式zookeeper集群禽笑,啟動(dòng)過程中如果前兩個(gè)啟動(dòng)的服務(wù)戶出現(xiàn)異常情況為正常,直到3個(gè)zkServer-x.cmd都啟動(dòng)完后就不會(huì)出現(xiàn)異常情況蛤奥。

進(jìn)入cmd執(zhí)行命令:netstat -ano # 查看端口情況
到此zk的偽分布式集群配置完畢佳镜!

注意:

同一IP上搭建多個(gè)節(jié)點(diǎn)的集群時(shí),必須要注意端口問題凡桥,端口必須不一致才行蟀伸;

創(chuàng)建多個(gè)節(jié)點(diǎn)集群時(shí),在dataDir目錄下必須創(chuàng)建myid文件,myid文件用于zookeeper驗(yàn)證server序號(hào)等望蜡,myid文件只有一行唤崭,并且為當(dāng)前server的序號(hào),例如server.1的myid就是1脖律,server2的myid就是2等谢肾。

server.A=B:C:D;其中 A 是一個(gè)數(shù)字,表示這個(gè)是第幾號(hào)服務(wù)器小泉;B 是這個(gè)服務(wù)器的 ip 地址芦疏;C 表示的是這個(gè)服務(wù)器與集群中的 Leader 服務(wù)器交換信息的端口;D 表示的是萬一集群中的 Leader 服務(wù)器掛了微姊,需要一個(gè)端口來重新進(jìn)行選舉酸茴,選出一個(gè)新的 Leader,而這個(gè)端口就是用來執(zhí)行選舉時(shí)服務(wù)器相互通信的端口兢交。如果是偽集群的配置方式薪捍,由于 B 都是一樣,所以不同的 Zookeeper 實(shí)例通信端口號(hào)不能一樣配喳,所以要給它們分配不同的端口號(hào)酪穿。


7、配置中出現(xiàn)的問題及解決方式:

  • 1.啟動(dòng)zkServer.cmd 時(shí) cmd命令窗口閃退

    這時(shí)可以修改zkServer.cmd文件內(nèi)容晴裹,在末尾加上 pause關(guān)鍵字被济,之后再啟動(dòng)cmd窗口不會(huì)閃退,且可看到啟動(dòng)時(shí)所拋出的異常涧团,可以根據(jù)異常 描述進(jìn)行相關(guān)處理只磷,我的異常-注解中文亂碼

  • 2.有myid還報(bào)錯(cuò)Caused by: java.lang.IllegalArgumentException: myid file is missing

    解決方式:將myid.txt 后綴去掉.txt,只保留myid文件名

  • 以上兩個(gè)異常只是本人在安裝過程中遇到的問題泌绣,可能還有其他其他異常我沒遇到钮追,下面是別人記錄的異常

  • 3.Error: JAVA_HOME is incorrectly set:缺少jdk或者jdk版本不匹配等相關(guān)問題

  • 4.java.net.bindexception address already:端口號(hào)被占用

  • 5.java.lang.numberformatexception:數(shù)字轉(zhuǎn)換異常,出現(xiàn)原因可能是myid文件內(nèi)容問題阿迈,或者在使用命令啟動(dòng)時(shí)在zkServer.cmd 多了其他內(nèi)容元媚,使用命令啟動(dòng)時(shí)如上圖所示選擇到zkServer.cmd即可,后面不需要任何內(nèi)容


三仿滔、Zookeeper真分布式集群(未測(cè)試)

多IP多節(jié)點(diǎn):部署在不同IP惠毁,各有自己的端口(未測(cè)試)
多IP多節(jié)點(diǎn):將zookeeper拷貝到每個(gè)節(jié)點(diǎn)一份。
多IP多節(jié)點(diǎn)與單IP多節(jié)點(diǎn)搭建過程基本一致崎页,上述過程不再重復(fù)描述鞠绰,僅重點(diǎn)說一個(gè)地方:server的IP地址、端口為真實(shí)即可飒焦。

注意:zk的部署個(gè)數(shù)最好為基數(shù)蜈膨,ZK集群的機(jī)制是只要超過半數(shù)的節(jié)點(diǎn)OK屿笼,集群就能正常提供服務(wù)。只有ZK節(jié)點(diǎn)掛得太多翁巍,只剩一半或不到一半節(jié)點(diǎn)能工作驴一,集群才失效。

1.Dubbo中zookeeper做注冊(cè)中心灶壶,如果注冊(cè)中心集群都掛掉肝断,發(fā)布者和訂閱者之間還能通信么?

啟動(dòng)dubbo時(shí)驰凛,消費(fèi)者會(huì)從zk拉取注冊(cè)的生產(chǎn)者的地址接口等數(shù)據(jù)胸懈,緩存在本地。每次調(diào)用時(shí)恰响,按照本地存儲(chǔ)的地址進(jìn)行調(diào)用趣钱。但是在注冊(cè)中心全部掛掉后增加新的提供者,則不能被消費(fèi)者發(fā)現(xiàn):

健狀性

  • 監(jiān)控中心宕掉不影響使用胚宦,只是丟失部分采樣數(shù)據(jù)
  • 數(shù)據(jù)庫宕掉后首有,注冊(cè)中心仍能通過緩存提供服務(wù)列表查詢,但不能注冊(cè)新服務(wù)
  • 注冊(cè)中心對(duì)等集群枢劝,任意一臺(tái)宕掉后井联,將自動(dòng)切換到另一臺(tái)
  • 注冊(cè)中心全部宕掉后,服務(wù)提供者和服務(wù)消費(fèi)者仍能通過本地緩存通訊
  • 服務(wù)提供者無狀態(tài)呈野,任意一臺(tái)宕掉后低矮,不影響使用
  • 服務(wù)提供者全部宕掉后印叁,服務(wù)消費(fèi)者應(yīng)用將無法使用被冒,并無限次重連等待服務(wù)提供者恢復(fù)

Dubbo默認(rèn)緩存的本地路徑:C:\Users\Administrator\.dubbo
因此,就算zk服務(wù)關(guān)掉轮蜕,也能繼續(xù)訪問項(xiàng)目昨悼。

四、驗(yàn)證服務(wù)器

利用 zktools可視化 工具
工具下載地址:zktools可視化 提取碼:zv2f

使用方式:先啟動(dòng)zk跃洛,再用 **zktools可視化 **連接指定的 ip:端口

zktools工具使用.png

Dubbo中zookeeper做注冊(cè)中心率触,如果注冊(cè)中心集群都掛掉,發(fā)布者和訂閱者之間還能通信么汇竭?

啟動(dòng)dubbo時(shí)葱蝗,消費(fèi)者會(huì)從zk拉取注冊(cè)的生產(chǎn)者的地址接口等數(shù)據(jù),緩存在本地细燎。每次調(diào)用時(shí)两曼,按照本地存儲(chǔ)的地址進(jìn)行調(diào)用。但是在注冊(cè)中心全部掛掉后增加新的提供者玻驻,則不能被消費(fèi)者發(fā)現(xiàn):

健狀性

  • 監(jiān)控中心宕掉不影響使用悼凑,只是丟失部分采樣數(shù)據(jù)
  • 數(shù)據(jù)庫宕掉后,注冊(cè)中心仍能通過緩存提供服務(wù)列表查詢,但不能注冊(cè)新服務(wù)
  • 注冊(cè)中心對(duì)等集群户辫,任意一臺(tái)宕掉后渐夸,將自動(dòng)切換到另一臺(tái)
  • 注冊(cè)中心全部宕掉后,服務(wù)提供者和服務(wù)消費(fèi)者仍能通過本地緩存通訊
  • 服務(wù)提供者無狀態(tài)渔欢,任意一臺(tái)宕掉后墓塌,不影響使用
  • 服務(wù)提供者全部宕掉后,服務(wù)消費(fèi)者應(yīng)用將無法使用奥额,并無限次重連等待服務(wù)提供者恢復(fù)

五桃纯、springboot zookeeperk dubbo

這里只介紹怎么配置,不過多詳細(xì)介紹項(xiàng)目如何搭建及詳細(xì)代碼披坏。下面調(diào)用的是二态坦、Zookeeper 偽分布式安裝(集群) 配置的端口:2183、2184棒拂、2185

1伞梯、zk連接測(cè)試

這里只是zk測(cè)試,不需要搭建dubbo+zk分部署項(xiàng)目帚屉。

pom.xml依賴
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>
測(cè)試集群是否搭建成功:

測(cè)試類 ZooKeeperHello.java

package com.dist.zk;

import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.Test;

import java.io.IOException;

/**
 * @author zhengja@dist.com.cn
 * @data 2019/8/21 15:21
 */
public class ZooKeeperHello {

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        ZooKeeper zk = new ZooKeeper("192.168.2.113:2183", 300000, new DemoWatcher());//連接zk server
        String node = "/app1";
        Stat stat = zk.exists(node, false);//檢測(cè)/app1是否存在
        if (stat == null) {
            //創(chuàng)建節(jié)點(diǎn)
            String createResult = zk.create(node, "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println(createResult);
        }
        //獲取節(jié)點(diǎn)的值
        byte[] b = zk.getData(node, false, stat);
        System.out.println(new String(b));
        zk.close();
    }

    static class DemoWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            System.out.println("----------->");
            System.out.println("path:" + event.getPath());
            System.out.println("type:" + event.getType());
            System.out.println("stat:" + event.getState());
            System.out.println("<-----------");
        }
    }

    /**Spring Boot2.0之 整合Zookeeper集群
     * 普通的連接:
     */
    @Test
    public void testZkClient(){
        String connection = "192.168.2.113:2183,192.168.2.113:2184,192.168.2.113:2185";
        ZkClient zkClient = new ZkClient(connection);
        zkClient.createPersistent("/toov5_01");  //添加節(jié)點(diǎn)
        zkClient.close();
    }

}

執(zhí)行測(cè)試后谜诫,使用zktools可視化工具查看是否成功。

工具下載地址:zktools可視化 提取碼:zv2f

使用方式:先啟動(dòng)zk攻旦,再用 **zktools可視化 **連接指定的 ip:端口

zktools工具使用.png

測(cè)試:zk端口2183:

zktools工具使用2.png

zk端口2184:

zktools工具使用3.png

在zk端口2185也添加節(jié)點(diǎn)成功喻旷,說明集群搭建成功。

2牢屋、單機(jī)測(cè)試-分布式(dubbo)

需要搭建springboot+zookeeper+dubbo分布式測(cè)試項(xiàng)目

web層(消費(fèi)者)配置
1).web層(消費(fèi)者)- 引入依賴pom.xml
<!--dubbo依賴-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>

2).web層(消費(fèi)者)- *.yml 文件
# dubbo配置
dubbo:
  application:
    name: consumer # 消費(fèi)方應(yīng)用名且预,用于計(jì)算依賴關(guān)系,不要與提供方一樣
  registry:
    protocol: zookeeper   #dubbo/zookeeper 協(xié)議
    #address: 127.0.0.1:2183    # zookeeper協(xié)議配置
    address: 192.168.2.113:2183    # zookeeper協(xié)議配置 測(cè)試ip連接烙无,zk是否支持遠(yuǎn)程調(diào)用
  interface:
    version: 1.0.0  # 接口版本號(hào)
  annotation:
    package: com.dist  # dubbo注解掃描包锋谐,注意更改成功自己項(xiàng)目的java文件路徑,否則注冊(cè)不到服務(wù)
  consumer:
    timeout: 50000  # 超時(shí)時(shí)間
    check: false   # check校驗(yàn):閉所有服務(wù)的啟動(dòng)時(shí)檢查
    version: 1.0.0 # dubbo默認(rèn)版本號(hào)截酷,在url顯示為default.version=xxx
3).web層(消費(fèi)者)- spring-dubbo-consumer.xml

將spring-dubbo-consumer.xml放置resources/config下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--加載 application*.yml 資源-->
    <context:property-placeholder location="classpath:application*.yml" ignore-unresolvable="true"/>
    <!--消費(fèi)方應(yīng)用名涮拗,用于計(jì)算依賴關(guān)系,不是匹配條件迂苛,不要與提供方一樣-->
    <dubbo:application name="${dubbo.application.name}"/>
    <!--zk 注冊(cè)中心暴露服務(wù)地址三热,協(xié)議-->
    <dubbo:registry id="zk" address="${dubbo.registry.address}" protocol="${dubbo.registry.protocol}"/>
    <!--dubbo 掃描包位置-->
    <dubbo:annotation package="${dubbo.annotation.package}"/>
    <!--dubbo 版本,超時(shí)三幻,check校驗(yàn):關(guān)閉所有服務(wù)的啟動(dòng)時(shí)檢查-->
    <dubbo:consumer version="${dubbo.consumer.version}" timeout="${dubbo.consumer.timeout}" check="${dubbo.consumer.check}"/>
</beans>
4).web層(消費(fèi)者)- 需要引入api層(公共接口)依賴jar

在pom.xml添加

<!--api依賴-->
<dependency>
    <groupId>com.dist</groupId>
    <artifactId>springboot-test-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
5).web層(消費(fèi)者)- 測(cè)試代碼:

創(chuàng)建DubboTestService.java 接口

public interface DubboTestService {
    String getData(String data);
}
service層(提供者)配置
1).service層(提供者)- pom.xml
<!--dubbo-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--dubbo-zk-curator-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.0.1</version>
</dependency>
2).service層(提供者)- *.yml文件配置
# dubbo配置
dubbo:
  application:
    name: provider  # 提供方應(yīng)用名就漾,用于計(jì)算依賴關(guān)系,不要與消費(fèi)方一樣
  registry:
    protocol: zookeeper  #dubbo/zookeeper 協(xié)議
    address: 127.0.0.1:2183  # zookeeper協(xié)議配置方式
    #address: 192.168.2.113:2183    # zookeeper協(xié)議配置 測(cè)試ip連接赌髓,zk是否支持遠(yuǎn)程調(diào)用
    #address: zookeeper://127.0.0.1:2183  # dubbo協(xié)議配置
  protocol:
    port: 30103  # dubbo協(xié)議缺省port端口20880从藤,多個(gè)提供者會(huì)沖突
  annotation:
    package: com.dist.server  # dubbo注解掃描包催跪,注意更改成功自己項(xiàng)目的java文件路徑,否則注冊(cè)不到服務(wù)
  provider:
    version: 1.0.0 #dubbo默認(rèn)版本號(hào)夷野,在url顯示為default.version=xxx
3).service層(提供者)- spring-dubbo-provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--加載 application*.yml 資源-->
    <context:property-placeholder location="classpath:application*.yml" ignore-unresolvable="true"/>
    <!--提供方應(yīng)用名懊蒸,用于計(jì)算依賴關(guān)系,不是匹配條件悯搔,不要與消費(fèi)方一樣-->
    <dubbo:application name="${dubbo.application.name}"/>
    <!--zk 注冊(cè)中心暴露服務(wù)地址,協(xié)議:dubbo/zookeeper,dubbo協(xié)議缺省port端口20880骑丸,多個(gè)提供者會(huì)沖突 添加 port="${dubbo.protocol.port}-->
    <dubbo:registry address="${dubbo.registry.address}" protocol="${dubbo.registry.protocol}"/>
    <!--dubbo 版本,超時(shí)-->
    <dubbo:provider version="${dubbo.provider.version}" timeout="50000" />
    <!--dubbo 掃描包位置-->
    <dubbo:annotation package="${dubbo.annotation.package}"/>
</beans>
4).service層(提供者)- 需要引入api層(公共接口)依賴jar

在pom.xml添加

<!--api依賴-->
<dependency>
    <groupId>com.dist</groupId>
    <artifactId>springboot-test-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
5).service層(提供者)- 測(cè)試代碼:

DubboTestServiceImpl.java實(shí)現(xiàn)類

import com.alibaba.dubbo.config.annotation.Service;
@Service
public class DubboTestServiceImpl implements DubboTestService {
    @Override
    public String getData(String data){
        return "service層返回的data數(shù)據(jù):"+data;
    }
}
api層(公共接口)

api層提供公共接口妒貌,web層調(diào)用和service層實(shí)現(xiàn)通危,需要兩者都引入api層的依賴jar:

DubboTestService.java 公共接口

public interface DubboTestService {
    String getData(String data);
}

3、偽集群測(cè)試-分布式(dubbo)

2灌曙、單機(jī)測(cè)試-分布式(dubbo) 基礎(chǔ)上更改 *.yml 配置:

1.web層(消費(fèi)者)- *.yml文件配置:zookeeper集群配置
# dubbo配置
dubbo:
  application:
    name: consumer
  registry:
    protocol: zookeeper   #dubbo/zookeeper 協(xié)議
    #address: 127.0.0.1:2183    # zookeeper協(xié)議配置
    #address: 192.168.2.113:2183    # zookeeper協(xié)議配置 測(cè)試ip連接菊碟,zk是否支持遠(yuǎn)程調(diào)用
    #address: zookeeper://127.0.0.1:2183  # dubbo協(xié)議配置
    #address: zookeeper://127.0.0.1:2183?backup=127.0.0.1:2184,127.0.0.1:2185  # dubbo協(xié)議配置集群-zk主從配置方法
    address: 127.0.0.1:2183,127.0.0.1:2184,127.0.0.1:2185  # zookeeper協(xié)議配置集群-zk非主從配置方法
  interface:
    version: 1.0.0
  annotation:
    package: com.dist  # 掃描包
  consumer:
    timeout: 50000
    check: false
    version: 1.0.0 #dubbo默認(rèn)版本號(hào),在url顯示為default.version=xxx
2.service層(提供者)- *.yml配置:集群配置
# dubbo配置
dubbo:
  application:
    name: provider
  registry:
    protocol: zookeeper  #dubbo/zookeeper 協(xié)議
    #address: 127.0.0.1:2183  # zookeeper協(xié)議配置方式
    #address: 192.168.2.113:2183    # zookeeper協(xié)議配置 測(cè)試ip連接在刺,zk是否支持遠(yuǎn)程調(diào)用
    #address: zookeeper://127.0.0.1:2183  # dubbo協(xié)議配置
    #address: zookeeper://127.0.0.1:2183?backup=127.0.0.1:2184,127.0.0.1:2185  # dubbo協(xié)議配置集群-zk主從配置方法
    address: 127.0.0.1:2183,127.0.0.1:2184,127.0.0.1:2185  # zookeeper協(xié)議配置集群-zk非主從配置方法
  protocol:
    port: 30103
  annotation:
    package: com.dist.server
  provider:
    version: 1.0.0 #dubbo默認(rèn)版本號(hào)逆害,在url顯示為default.version=xxx

到這里偽集群測(cè)試-配置完成!

備注:

  • web層協(xié)議和service協(xié)議最好配置一樣蚣驼,protocol: dubbo/zookeeper # 協(xié)議當(dāng)然兩者配置不一致也可以調(diào)用


六魄幕、Zookeeper安全認(rèn)證

1、為什么Zookeeper要安全認(rèn)證 颖杏?

1).服務(wù)都是在內(nèi)網(wǎng)纯陨,Zookeeper集群配置都是走的內(nèi)網(wǎng)IP,外網(wǎng)不開放相關(guān)端口留储,不需要zookeeper對(duì)外開放翼抠。但是可能由于業(yè)務(wù)升級(jí),例如購置了阿里云的服務(wù)欲鹏,需要對(duì)外開放Zookeeper服務(wù)机久,就需要對(duì)zookeeper進(jìn)行安全認(rèn)證了臭墨。

2).Zookeeper 未授權(quán)訪問(中危赔嚎,3處)


zk認(rèn)證1.png

2.ACL認(rèn)證的簡(jiǎn)介

首先說明一下為什么需要ACL

簡(jiǎn)單來說 :在通常情況下,zookeeper允許未經(jīng)授權(quán)的訪問,因此在安全漏洞掃描中暴漏未授權(quán)訪問漏洞。這在一些監(jiān)控很嚴(yán)的系統(tǒng)中是不被允許的,所以需要ACL來控制權(quán)限.

既然需要ACL來控制權(quán)限,那么Zookeeper的權(quán)限有哪些呢?
權(quán)限包括以下幾種:

CREATE: 能創(chuàng)建子節(jié)點(diǎn)
READ:能獲取節(jié)點(diǎn)數(shù)據(jù)和列出其子節(jié)點(diǎn)
WRITE: 能設(shè)置節(jié)點(diǎn)數(shù)據(jù)
DELETE: 能刪除子節(jié)點(diǎn)
ADMIN: 能設(shè)置權(quán)限

說到權(quán)限,介紹一下zookeeper的四種認(rèn)證方式:

world:默認(rèn)方式胧弛,相當(dāng)于全世界都能訪問
auth:代表已經(jīng)認(rèn)證通過的用戶(cli中可以通過addauth digest user:pwd 來添加當(dāng)前上下文中的授權(quán)用戶)
digest:即用戶名:密碼這種方式認(rèn)證尤误,這也是業(yè)務(wù)系統(tǒng)中最常用的
ip:使用Ip地址認(rèn)證

ACL基本介紹就到這里。

3.沒有ACL認(rèn)證時(shí)zookeeper的操作

直接上代碼 : 更改一下服務(wù)器地址和端口號(hào)即可!
pom.xml

<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.8</version>
   <scope>test</scope>
</dependency>
import java.io.IOException;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

public class ZkConn {
     public static void main(String[] args) 
             throws IOException, KeeperException, InterruptedException {
     /**
      *  創(chuàng)建一個(gè)與服務(wù)器的連接
      *  參數(shù)一:服務(wù)器地址和端口號(hào)(該端口號(hào)值服務(wù)器允許客戶端連接的端口號(hào))
      *  參數(shù)二:連接會(huì)話超時(shí)時(shí)間
      *  參數(shù)三:觀察者结缚,連接成功會(huì)觸發(fā)該觀察者损晤。不過只會(huì)觸發(fā)一次。
      *      該Watcher會(huì)獲取各種事件的通知
      */
     ZooKeeper zk = new ZooKeeper("node005:4180", 60000, new Watcher() {
         // 監(jiān)控所有被觸發(fā)的事件
         public void process(WatchedEvent event) {
             System.out.println("監(jiān)控所有被觸發(fā)的事件:EVENT:" + event.getType());
         }
     });
     System.out.println("*******************************************************");
     // 查看根節(jié)點(diǎn)的子節(jié)點(diǎn)
     System.out.println("查看根節(jié)點(diǎn)的子節(jié)點(diǎn):ls / => " + zk.getChildren("/", true));
     System.out.println("*******************************************************");
     // 創(chuàng)建一個(gè)目錄節(jié)點(diǎn)
     if (zk.exists("/node", true) == null) {
         /**
          * 參數(shù)一:路徑地址
          * 參數(shù)二:想要保存的數(shù)據(jù)红竭,需要轉(zhuǎn)換成字節(jié)數(shù)組
          * 參數(shù)三:ACL訪問控制列表(Access control list),
          *      參數(shù)類型為ArrayList<ACL>尤勋,Ids接口提供了一些默認(rèn)的值可以調(diào)用喘落。
          *      OPEN_ACL_UNSAFE     This is a completely open ACL 
          *                          這是一個(gè)完全開放的ACL,不安全
          *      CREATOR_ALL_ACL     This ACL gives the
          *                           creators authentication id's all permissions.
          *                          這個(gè)ACL賦予那些授權(quán)了的用戶具備權(quán)限
          *      READ_ACL_UNSAFE     This ACL gives the world the ability to read.
          *                          這個(gè)ACL賦予用戶讀的權(quán)限最冰,也就是獲取數(shù)據(jù)之類的權(quán)限瘦棋。
          * 參數(shù)四:創(chuàng)建的節(jié)點(diǎn)類型。枚舉值CreateMode
          *      PERSISTENT (0, false, false)
          *      PERSISTENT_SEQUENTIAL (2, false, true)
          *          這兩個(gè)類型創(chuàng)建的都是持久型類型節(jié)點(diǎn)暖哨,回話結(jié)束之后不會(huì)自動(dòng)刪除赌朋。
          *          區(qū)別在于,第二個(gè)類型所創(chuàng)建的節(jié)點(diǎn)名后會(huì)有一個(gè)單調(diào)遞增的數(shù)值
          *      EPHEMERAL (1, true, false)
          *      EPHEMERAL_SEQUENTIAL (3, true, true)
          *          這兩個(gè)類型所創(chuàng)建的是臨時(shí)型類型節(jié)點(diǎn)篇裁,在回話結(jié)束之后沛慢,自動(dòng)刪除。
          *          區(qū)別在于达布,第二個(gè)類型所創(chuàng)建的臨時(shí)型節(jié)點(diǎn)名后面會(huì)有一個(gè)單調(diào)遞增的數(shù)值团甲。
          * 最后create()方法的返回值是創(chuàng)建的節(jié)點(diǎn)的實(shí)際路徑
          */
         zk.create("/node", "conan".getBytes(),
                 Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
         System.out.println("創(chuàng)建一個(gè)目錄節(jié)點(diǎn):create /node conan");
         /**
          *  查看/node節(jié)點(diǎn)數(shù)據(jù),這里應(yīng)該輸出"conan"
          *  參數(shù)一:獲取節(jié)點(diǎn)的路徑
          *  參數(shù)二:說明是否需要觀察該節(jié)點(diǎn)黍聂,設(shè)置為true伐庭,則設(shè)定共享默認(rèn)的觀察器
          *  參數(shù)三:stat類,保存節(jié)點(diǎn)的信息分冈。例如數(shù)據(jù)版本信息圾另,創(chuàng)建時(shí)間,修改時(shí)間等信息
          */
         System.out.println("查看/node節(jié)點(diǎn)數(shù)據(jù):get /node => "
                 + new String(zk.getData("/node", false, null)));
         /**
          * 查看根節(jié)點(diǎn)
          * 在此查看根節(jié)點(diǎn)的值雕沉,這里應(yīng)該輸出上面所創(chuàng)建的/node節(jié)點(diǎn)
          */
         System.out.println("查看根節(jié)點(diǎn):ls / => " + zk.getChildren("/", true));
     }
     System.out.println("*******************************************************");
     // 創(chuàng)建一個(gè)子目錄節(jié)點(diǎn)
     if (zk.exists("/node/sub1", true) == null) {
         zk.create("/node/sub1", "sub1".getBytes(),
                 Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
         System.out.println("創(chuàng)建一個(gè)子目錄節(jié)點(diǎn):create /node/sub1 sub1");
         // 查看node節(jié)點(diǎn)
         System.out.println("查看node節(jié)點(diǎn):ls /node => "
                 + zk.getChildren("/node", true));
     }
     System.out.println("*******************************************************");
     /**
      *  修改節(jié)點(diǎn)數(shù)據(jù)
      *  修改的數(shù)據(jù)會(huì)覆蓋上次所設(shè)置的數(shù)據(jù)
      *  setData()方法參數(shù)一集乔、參數(shù)二不多說,與上面類似坡椒。
      *  參數(shù)三:數(shù)值型扰路。需要傳入該界面的數(shù)值類型版本號(hào)!>蟮稹汗唱!
      *      該信息可以通過Stat類獲取,也可以通過命令行獲取丈攒。
      *      如果該值設(shè)置為-1哩罪,就是忽視版本匹配,直接設(shè)置節(jié)點(diǎn)保存的值巡验。
      */
     if (zk.exists("/node", true) != null) {
         zk.setData("/node", "changed".getBytes(), -1);
         // 查看/node節(jié)點(diǎn)數(shù)據(jù)
         System.out.println("修改節(jié)點(diǎn)數(shù)據(jù):get /node => "
                 + new String(zk.getData("/node", false, null)));
     }
     System.out.println("*******************************************************");
     // 刪除節(jié)點(diǎn)
     if (zk.exists("/node/sub1", true) != null) {
         zk.delete("/node/sub1", -1);
         zk.delete("/node", -1);
         // 查看根節(jié)點(diǎn)
         System.out.println("刪除節(jié)點(diǎn):ls / => " + zk.getChildren("/", true));
     }
     // 關(guān)閉連接
     zk.close();
 }
}


認(rèn)證只是針對(duì)一個(gè)節(jié)點(diǎn)

  • 認(rèn)證只是針對(duì)一個(gè)節(jié)點(diǎn)

  • ACL【Access Control List】际插,ZooKeeper作為一個(gè)分布式協(xié)調(diào)框架,其內(nèi)部存儲(chǔ)的都是一些關(guān)乎分布式系統(tǒng)運(yùn)行時(shí)狀態(tài)的元數(shù)據(jù)显设,尤其是涉及到一些分布式鎖框弛,Master選舉和協(xié)調(diào)等應(yīng)用場(chǎng)景。我們需要有效的保障ZooKeeper中的數(shù)據(jù)安全捕捂,ZooKeeper提供了三種模式瑟枫。權(quán)限模式斗搞、授權(quán)對(duì)象、權(quán)限慷妙。

  • 權(quán)限模式:Scheme榜旦,開發(fā)人員最多使用的如下四種權(quán)限模式:
    IP:IP模式通過IP地址粒度來進(jìn)行控制權(quán)限,例如配置了:IP景殷,192.168.1.107即表示權(quán)限控制都是針對(duì)這個(gè)IP地址的溅呢,同時(shí)也支持按網(wǎng)段分配,比如:192.168.1.*

    Digest:digest是最常用的權(quán)限控制模式猿挚,也更符合我們對(duì)權(quán)限控制的認(rèn)識(shí)咐旧,其類似于“username:password”形式的權(quán)限標(biāo)識(shí)進(jìn)行權(quán)限配置。ZooKeeper會(huì)對(duì)形式的權(quán)限標(biāo)識(shí)先后進(jìn)行兩次編碼處理绩蜻,分別是SHA-1加密算法铣墨,BASE64編碼

    World:World是一直最開放的權(quán)限控制模式。這種控制模式可以看做為特殊的Digest办绝,它僅僅是一個(gè)標(biāo)識(shí)而已

    Super:超級(jí)用戶模式伊约,在超級(jí)用戶模式下可以對(duì)ZooKeeper任意進(jìn)行操作

  • 授權(quán)對(duì)象:指的是權(quán)限賦予的用戶或者一個(gè)指定的實(shí)體,例如IP地址或者機(jī)器等孕蝉。在不同的模式下屡律,授權(quán)對(duì)象是不同的。這種模式和權(quán)限對(duì)象一一對(duì)應(yīng)

  • 權(quán)限:權(quán)限就是指那些通過權(quán)限檢測(cè)后可以被允許執(zhí)行的操作,在ZooKeeper中,對(duì)數(shù)據(jù)的操作權(quán)限分為以下五個(gè)大類:CREATE扶叉、DELETE溪窒、READ葱跋、WRITE、ADMIN


4.有ACL認(rèn)證時(shí)zookeeper的操作測(cè)試:

pom.xml

<dependency>
         <groupId>org.apache.zookeeper</groupId>
         <artifactId>zookeeper</artifactId>
        <version>3.4.8</version>
         <scope>test</scope>
     </dependency>

代碼類:Zookeeper 節(jié)點(diǎn)授權(quán)

package com.dist;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**Zookeeper 節(jié)點(diǎn)授權(quán)
 * 這里測(cè)試的認(rèn)證方式:digest  相當(dāng)于  user:pass 認(rèn)證
 *
 * @author zhengja@dist.com.cn
 * @data 2019/8/22 15:29
 */
public class ZookeeperAuth implements Watcher {

    /** 連接地址 */
    final static String CONNECT_ADDR = "127.0.0.1:2183";
    /** 測(cè)試路徑 */
    final static String PATH = "/testAuth";
    final static String PATH_DEL = "/testAuth/delNode";
    /** 認(rèn)證類型 */
    final static String authentication_type = "digest";
    /** 認(rèn)證正確方法 */
    final static String correctAuthentication = "123456";
    /** 認(rèn)證錯(cuò)誤方法 */
    final static String badAuthentication = "654321";

    static ZooKeeper zk = null;
    /** 計(jì)時(shí)器 */
    AtomicInteger seq = new AtomicInteger();
    /** 標(biāo)識(shí) */
    private static final String LOG_PREFIX_OF_MAIN = "【Main】";

    private CountDownLatch connectedSemaphore = new CountDownLatch(1);

    @Override
    public void process(WatchedEvent event) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (event==null) {
            return;
        }
        // 連接狀態(tài)
        Event.KeeperState keeperState = event.getState();
        // 事件類型
        Event.EventType eventType = event.getType();
        // 受影響的path
        String path = event.getPath();

        String logPrefix = "【W(wǎng)atcher-" + this.seq.incrementAndGet() + "】";

        System.out.println(logPrefix + "收到Watcher通知");
        System.out.println(logPrefix + "連接狀態(tài):\t" + keeperState.toString());
        System.out.println(logPrefix + "事件類型:\t" + eventType.toString());
        if (Event.KeeperState.SyncConnected == keeperState) {
            // 成功連接上ZK服務(wù)器
            if (Event.EventType.None == eventType) {
                System.out.println(logPrefix + "成功連接上ZK服務(wù)器");
                connectedSemaphore.countDown();
            }
        } else if (Event.KeeperState.Disconnected == keeperState) {
            System.out.println(logPrefix + "與ZK服務(wù)器斷開連接");
        } else if (Event.KeeperState.AuthFailed == keeperState) {
            System.out.println(logPrefix + "權(quán)限檢查失敗");
        } else if (Event.KeeperState.Expired == keeperState) {
            System.out.println(logPrefix + "會(huì)話失效");
        }
        System.out.println("--------------------------------------------");
    }
    /**
     * 創(chuàng)建ZK連接
     *
     * @param connectString
     *            ZK服務(wù)器地址列表
     * @param sessionTimeout
     *            Session超時(shí)時(shí)間
     */
    public void createConnection(String connectString, int sessionTimeout) {
        this.releaseConnection();
        try {
            zk = new ZooKeeper(connectString, sessionTimeout, this);
            //添加節(jié)點(diǎn)授權(quán)
            zk.addAuthInfo(authentication_type,correctAuthentication.getBytes());
            System.out.println(LOG_PREFIX_OF_MAIN + "開始連接ZK服務(wù)器");
            //倒數(shù)等待
            connectedSemaphore.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 關(guān)閉ZK連接
     */
    public void releaseConnection() {
        if (this.zk!=null) {
            try {
                this.zk.close();
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     *
     * <B>方法名稱:</B>測(cè)試函數(shù)<BR>
     * <B>概要說明:</B>測(cè)試認(rèn)證<BR>
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        ZookeeperAuth testAuth = new ZookeeperAuth();
        testAuth.createConnection(CONNECT_ADDR,2000);
        List<ACL> acls = new ArrayList<ACL>(1);
        for (ACL ids_acl : ZooDefs.Ids.CREATOR_ALL_ACL) {
            acls.add(ids_acl);
        }

        try {
            zk.create(PATH, "init content".getBytes(), acls, CreateMode.PERSISTENT);
            System.out.println("使用授權(quán)key:" + correctAuthentication + "創(chuàng)建節(jié)點(diǎn):"+ PATH + ", 初始內(nèi)容是: init content");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            zk.create(PATH_DEL, "will be deleted! ".getBytes(), acls, CreateMode.PERSISTENT);
            System.out.println("使用授權(quán)key:" + correctAuthentication + "創(chuàng)建節(jié)點(diǎn):"+ PATH_DEL + ", 初始內(nèi)容是: will be deleted!");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 獲取數(shù)據(jù)
        getDataByNoAuthentication();  //獲取數(shù)據(jù):不采用密碼
        getDataByBadAuthentication(); //獲取數(shù)據(jù):采用錯(cuò)誤的密碼 
        getDataByCorrectAuthentication();  //采用正確的密碼

        // 更新數(shù)據(jù)
        updateDataByNoAuthentication();  //更新數(shù)據(jù):不采用密碼
        updateDataByBadAuthentication();  //更新數(shù)據(jù):采用錯(cuò)誤的密碼
        updateDataByCorrectAuthentication();  //更新數(shù)據(jù):采用正確的密碼

        // 刪除數(shù)據(jù)
        deleteNodeByNoAuthentication();  //不使用密碼 刪除節(jié)點(diǎn)
        deleteNodeByBadAuthentication(); //采用錯(cuò)誤的密碼刪除節(jié)點(diǎn)
        deleteNodeByCorrectAuthentication();  //使用正確的密碼刪除節(jié)點(diǎn)
        
        //線程等待
        Thread.sleep(1000);

        //使用正確的密碼刪除父節(jié)點(diǎn)
        deleteParent();
        
        //釋放連接
        testAuth.releaseConnection();
    }

    /** 獲取數(shù)據(jù):采用錯(cuò)誤的密碼 */
    static void getDataByBadAuthentication() {
        String prefix = "[使用錯(cuò)誤的授權(quán)信息]";
        try {
            ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            //授權(quán)
            badzk.addAuthInfo(authentication_type,badAuthentication.getBytes());
            Thread.sleep(2000);
            System.out.println(prefix + "獲取數(shù)據(jù):" + PATH);
            System.out.println(prefix + "成功獲取數(shù)據(jù):" + badzk.getData(PATH, false, null));
        } catch (Exception e) {
            System.err.println(prefix + "獲取數(shù)據(jù)失敗,原因:" + e.getMessage());
        }
    }

    /** 獲取數(shù)據(jù):不采用密碼 */
    static void getDataByNoAuthentication() {
        String prefix = "[不使用任何授權(quán)信息]";
        try {
            System.out.println(prefix + "獲取數(shù)據(jù):" + PATH);
            ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            Thread.sleep(2000);
            System.out.println(prefix + "成功獲取數(shù)據(jù):" + nozk.getData(PATH, false, null));
        } catch (Exception e) {
            System.err.println(prefix + "獲取數(shù)據(jù)失敗,原因:" + e.getMessage());
        }
    }

    /** 采用正確的密碼 */
    static void getDataByCorrectAuthentication() {
        String prefix = "[使用正確的授權(quán)信息]";
        try {
            System.out.println(prefix + "獲取數(shù)據(jù):" + PATH);

            System.out.println(prefix + "成功獲取數(shù)據(jù):" + zk.getData(PATH, false, null));
        } catch (Exception e) {
            System.out.println(prefix + "獲取數(shù)據(jù)失敗来庭,原因:" + e.getMessage());
        }
    }

    /**
     * 更新數(shù)據(jù):不采用密碼
     */
    static void updateDataByNoAuthentication() {

        String prefix = "[不使用任何授權(quán)信息]";

        System.out.println(prefix + "更新數(shù)據(jù): " + PATH);
        try {
            ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            Thread.sleep(2000);
            Stat stat = nozk.exists(PATH, false);
            if (stat!=null) {
                nozk.setData(PATH, prefix.getBytes(), -1);
                System.out.println(prefix + "更新成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "更新失敗,原因是:" + e.getMessage());
        }
    }

    /**
     * 更新數(shù)據(jù):采用錯(cuò)誤的密碼
     */
    static void updateDataByBadAuthentication() {

        String prefix = "[使用錯(cuò)誤的授權(quán)信息]";

        System.out.println(prefix + "更新數(shù)據(jù):" + PATH);
        try {
            ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            //授權(quán)
            badzk.addAuthInfo(authentication_type,badAuthentication.getBytes());
            Thread.sleep(2000);
            Stat stat = badzk.exists(PATH, false);
            if (stat!=null) {
                badzk.setData(PATH, prefix.getBytes(), -1);
                System.out.println(prefix + "更新成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "更新失敗穿挨,原因是:" + e.getMessage());
        }
    }

    /**
     * 更新數(shù)據(jù):采用正確的密碼
     */
    static void updateDataByCorrectAuthentication() {

        String prefix = "[使用正確的授權(quán)信息]";

        System.out.println(prefix + "更新數(shù)據(jù):" + PATH);
        try {
            Stat stat = zk.exists(PATH, false);
            if (stat!=null) {
                zk.setData(PATH, prefix.getBytes(), -1);
                System.out.println(prefix + "更新成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "更新失敗月弛,原因是:" + e.getMessage());
        }
    }

    /**
     * 不使用密碼 刪除節(jié)點(diǎn)
     */
    static void deleteNodeByNoAuthentication() throws Exception {

        String prefix = "[不使用任何授權(quán)信息]";

        try {
            System.out.println(prefix + "刪除節(jié)點(diǎn):" + PATH_DEL);
            ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            Thread.sleep(2000);
            Stat stat = nozk.exists(PATH_DEL, false);
            if (stat!=null) {
                nozk.delete(PATH_DEL,-1);
                System.out.println(prefix + "刪除成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "刪除失敗,原因是:" + e.getMessage());
        }
    }

    /**
     * 采用錯(cuò)誤的密碼刪除節(jié)點(diǎn)
     */
    static void deleteNodeByBadAuthentication() throws Exception {

        String prefix = "[使用錯(cuò)誤的授權(quán)信息]";

        try {
            System.out.println(prefix + "刪除節(jié)點(diǎn):" + PATH_DEL);
            ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            //授權(quán)
            badzk.addAuthInfo(authentication_type,badAuthentication.getBytes());
            Thread.sleep(2000);
            Stat stat = badzk.exists(PATH_DEL, false);
            if (stat!=null) {
                badzk.delete(PATH_DEL, -1);
                System.out.println(prefix + "刪除成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "刪除失敗絮蒿,原因是:" + e.getMessage());
        }
    }

    /**
     * 使用正確的密碼刪除節(jié)點(diǎn)
     */
    static void deleteNodeByCorrectAuthentication() throws Exception {

        String prefix = "[使用正確的授權(quán)信息]";

        try {
            System.out.println(prefix + "刪除節(jié)點(diǎn):" + PATH_DEL);
            Stat stat = zk.exists(PATH_DEL, false);
            if (stat!=null) {
                zk.delete(PATH_DEL, -1);
                System.out.println(prefix + "刪除成功");
            }
        } catch (Exception e) {
            System.out.println(prefix + "刪除失敗尊搬,原因是:" + e.getMessage());
        }
    }

    /**
     * 使用正確的密碼刪除父節(jié)點(diǎn)
     */
    static void deleteParent() throws Exception {
        String prefix = "[使用正確的授權(quán)信息]";
        try {
            Stat stat = zk.exists(PATH_DEL, false);
            if (stat == null) {
                zk.delete(PATH, -1);
            }
        } catch (Exception e) {
            System.out.println(prefix + "刪除父節(jié)點(diǎn)失敗,原因是:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

5.zookeeper超級(jí)用戶配置(Windows/Linux)

七土涝、Zookeeper+Dubbo認(rèn)證

  • 可通過 < dubbo:registry username="admin" password="1234" /> 設(shè)置 zookeeper 登錄信息
  • 可通過 < dubbo:registry group="dubbo" /> 設(shè)置 zookeeper 的根節(jié)點(diǎn),不設(shè)置將使用無根樹

官網(wǎng)文檔第五條幌墓,明確說明了可以通過username和 password字段設(shè)置zookeeper 登錄信息但壮。

但是冀泻,如果在Zookeeper上通過digest方式設(shè)置ACL,然后在dubbo registry上配置相應(yīng)的用戶蜡饵、密碼弹渔,服務(wù)就注冊(cè)不到Zookeeper上了,會(huì)報(bào)KeeperErrorCode = NoAuth錯(cuò)誤溯祸。

看了下調(diào)用相關(guān)代碼肢专,發(fā)現(xiàn)注冊(cè)服務(wù)時(shí)所傳的ACL,而配置在dubbo上的焦辅,沒有發(fā)現(xiàn)被使用的地方(如果注冊(cè)中心是Zookeeper的話)博杖。

但是查閱ZookeeperRegistry相關(guān)源碼并沒有發(fā)現(xiàn)相關(guān)認(rèn)證的地方,搜遍全網(wǎng)很少有問類似的問題筷登,這個(gè)問題似乎并沒有多少人關(guān)注剃根。

大部分服務(wù)大都是部署在內(nèi)網(wǎng)的,基本很少對(duì)外網(wǎng)開放前方,然而Dubbo的zookeeper用戶權(quán)限認(rèn)證貌似真的不起作用狈醉,如果非要對(duì)外開放只能通過iptables或者firewall進(jìn)行IP Access Control,如果是阿里云服務(wù)器的話安全組也是個(gè)不錯(cuò)的選擇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惠险,一起剝皮案震驚了整個(gè)濱河市苗傅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌班巩,老刑警劉巖金吗,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異趣竣,居然都是意外死亡摇庙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門遥缕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卫袒,“玉大人,你說我怎么就攤上這事单匣∠δ” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵户秤,是天一觀的道長(zhǎng)码秉。 經(jīng)常有香客問我,道長(zhǎng)鸡号,這世上最難降的妖魔是什么转砖? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上府蔗,老公的妹妹穿的比我還像新娘晋控。我一直安慰自己,他們只是感情好姓赤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布赡译。 她就那樣靜靜地躺著,像睡著了一般不铆。 火紅的嫁衣襯著肌膚如雪蝌焚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天誓斥,我揣著相機(jī)與錄音只洒,去河邊找鬼。 笑死岖食,一個(gè)胖子當(dāng)著我的面吹牛红碑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泡垃,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼析珊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蔑穴?” 一聲冷哼從身側(cè)響起忠寻,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎存和,沒想到半個(gè)月后奕剃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捐腿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年纵朋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茄袖。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡操软,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宪祥,到底是詐尸還是另有隱情聂薪,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布蝗羊,位于F島的核電站藏澳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏耀找。R本人自食惡果不足惜翔悠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凉驻,春花似錦腻要、人聲如沸复罐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽效诅。三九已至胀滚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乱投,已是汗流浹背咽笼。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留戚炫,地道東北人剑刑。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像双肤,于是被迫代替她去往敵國和親施掏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348