目錄


Windows安裝zk版本為:zookeeper-3.4.14
- 1、單節(jié)點方式:部署在一臺服務(wù)器上
- 2、單IP多節(jié)點(偽集群):部署在同一IP,但是有多個節(jié)點,各有自己的端口
- 3、多IP多節(jié)點:部署在不同IP,各有自己的端口(未測試)
一、Zookeeper安裝(單機部署)
- 單節(jié)點方式:部署在一臺服務(wù)器上
1、下載地址:
鏈接:zookeeper-3.4.14.tar.gz 提取碼:l77v
下載完--解壓縮后的目錄:D:\zookeeper-3.4.14

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

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

3、打開D:\zookeeper-3.4.14\conf
把這個zoo_sample.cfg文件重命名為zoo.cfg

打開zoo.cfg : 注意最好不要把中文注釋復(fù)制進去,容易出現(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
# 默認斷開2181 ,這里更改端口為2182
clientPort=2182
ps:參數(shù)說明
- tickTime:心跳間隔,這個時間作為zookeeper服務(wù)器之間或zookeeper服務(wù)器與客戶端服務(wù)器維持心跳的時間間隔,即每隔 tickTime 時間就會發(fā)送一個心跳
- initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這里所說的客戶端不是用戶連接 Zookeeper 服務(wù)器的客戶端,而是 Zookeeper 服務(wù)器集群中連接到 Leader 的 Follower 服務(wù)器)初始化連接時最長能忍受多少個心跳時間間隔數(shù)。當已經(jīng)超過 5個心跳的時間(也就是 tickTime)長度后 Zookeeper 服務(wù)器還沒有收到客戶端的返回信息,那么表明這個客戶端連接失敗??偟臅r間長度就是 52000=10 秒
- syncLimit:這個配置項表示 Leader 與 Follower 之間發(fā)送消息,請求和相應(yīng)時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 22000=4 秒
- dataDir:zookeeper存儲數(shù)據(jù)的目錄,默認情況下,zookeeper的日志問價也會保存至該目錄
- clientPort:客戶端連接zookeeper的端口號
- server.A=B:C:D:其中 A 是一個整形數(shù)字,表示服務(wù)器下標;B 是這個服務(wù)器的 ip 地址;C 表示的是這個服務(wù)器與集群中的 Leader 服務(wù)器交換信息的端口;D 表示的是萬一集群中的 Leader 服務(wù)器掛了,需要一個端口來重新進行選舉,選出一個新的 Leader,而這個端口就是用來執(zhí)行選舉時服務(wù)器相互通信的端口。如果是偽集群的配置方式,由于 B 都是一樣,所以不同的 Zookeeper 實例通信端口號不能一樣,所以要給它們分配不同的端口號。
4、啟動zookeeper
進入D:\zookeeper-3.4.14\bin

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

啟動成功標識:binding to port 0.0.0.0/0.0.0.0:2182
cmd命令窗口閃退,這時可以修改zkServer.cmd文件內(nèi)容,在末尾加上 pause關(guān)鍵字,之后再啟動cmd窗口不會閃退,且可看到啟動時所拋出的異常,可以根據(jù)異常描述進行相關(guān)處理,我的異常-注解中文亂碼
5、測試zookeeper是否可用:
5.1 maven項目的pom.xml中先添加以下依賴項
<!--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 {
<!--更改成本機ip地址測試,進入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);//檢測/app1是否存在
if (stat == null) {
//創(chuàng)建節(jié)點
String createResult = zk.create(node, "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(createResult);
}
//獲取節(jié)點的值
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("<-----------");
}
}
}
控制臺打印效果:
----------->
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
單機部署完成,測試成功!
這個版本的zk默是支持遠程連接的,不需要配置本機ip地址,也能讓別人連接!
二、Zookeeper 偽分布式安裝(集群)
單IP多節(jié)點(偽集群):部署在同一IP,但是有多個節(jié)點,各有自己的端口。
偽分布式安裝就是在同一臺pc上安裝,安裝時使用同一個zookeeper包,多個配置文件分別配置為不同的端口.由于機器數(shù)量有限,這里采用偽分布式配置模擬集群配置。
zk 版本:Windows安裝zk版本為:zookeeper-3.4.14
下載鏈接:zookeeper-3.4.14.tar.gz 提取碼:l77v
1、解壓縮后的目錄
下載完--解壓縮后的目錄:D:\zookeeper-3.4.14

2、修改配置
進入conf目錄下把zoo_sample.cfg文件重名為:zoo.cfg,并修改配置為如下:注意最好不要把中文注釋復(fù)制進去,容易出現(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
# 默認斷開2181 ,這里更改端口為2182
clientPort=2182
添加tmp文件夾
添加tmp文件夾,在目錄D:\zookeeper-3.4.14\下:
D:\zookeeper-3.4.14\tmp

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

3、啟動zookeeper服務(wù):
進入目錄:D:\zookeeper-3.4.14\bin,雙擊執(zhí)行zkServer.cmd,這樣就啟動了zookeeper服務(wù)了
關(guān)閉zkServer窗口,zookeeper服務(wù)器也就關(guān)閉了
4、偽分布式安裝
偽分布式安裝就是在同一臺pc上安裝,安裝時使用同一個zookeeper包,多個配置文件分別配置為不同的端口。我這里配置3個偽服務(wù)。
1.)將D:\zookeeper-3.4.14\conf\下的zoo.cfg分別復(fù)制出文件zoo1.cfg,zoo2.cfg,zoo3.cfg三個文件,并分別修改配置為:

分別修改配置為:
zoo1.cfg 注意最好不要把中文注釋復(fù)制進去,容易出現(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
# 默認斷開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ù)字表示第幾號服務(wù)器;ip/domain :是服務(wù)器域名或者ip地址。
Port1:表示這個服務(wù)器和集群中的Leader服務(wù)器交換信息的端口;
Port2:表示萬一集群中的Leader服務(wù)器掛了,需要一個端口重新進行選舉,選出一個新的Leader,這個端口就是用來執(zhí)行選舉時服務(wù)器相互通信的端口。
? 由于我們是偽集群,所以ip或者域名是一樣的,所以要分配不同的端口號
? server.A=B:C:D:其中 A 是一個整形數(shù)字,表示服務(wù)器下標,與myid文件中的id是一致的;B 是這個服務(wù)器的 ip 地址;C 表示的是這個服務(wù)器與集群中的 Leader 服務(wù)器交換信息的端口;D 表示的是萬一集群中的 Leader 服務(wù)器掛了,需要一個端口來重新進行選舉,選出一個新的 Leader,而這個端口就是用來執(zhí)行選舉時服務(wù)器相互通信的端口。如果是偽集群的配置方式,由于 B 都是一樣,所以不同的 Zookeeper 實例通信端口號不能一樣,所以要給它們分配不同的端口號。
創(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

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

創(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

5、修改zkServer.cmd
進入D:\zookeeper-3.4.14\bin下復(fù)制文件zkServer.cmd為zkServer-1.cmd,zkServer-2.cmd,zkServer-3.cmd

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、配置完成 啟動zkServer
分別啟動zkServer-1.cmd,zkServer-2.cmd,zkServer-3.cmd
進入目錄:D:\zookeeper-3.4.14\bin,分布執(zhí)行zkServer-1.cmd,zkServer-2.cmd,zkServer-3.cmd,啟動偽分布式zookeeper集群,啟動過程中如果前兩個啟動的服務(wù)戶出現(xiàn)異常情況為正常,直到3個zkServer-x.cmd都啟動完后就不會出現(xiàn)異常情況。
進入cmd執(zhí)行命令:netstat -ano # 查看端口情況
到此zk的偽分布式集群配置完畢!
注意:
同一IP上搭建多個節(jié)點的集群時,必須要注意端口問題,端口必須不一致才行;
創(chuàng)建多個節(jié)點集群時,在dataDir目錄下必須創(chuàng)建myid文件,myid文件用于zookeeper驗證server序號等,myid文件只有一行,并且為當前server的序號,例如server.1的myid就是1,server2的myid就是2等。
server.A=B:C:D;其中 A 是一個數(shù)字,表示這個是第幾號服務(wù)器;B 是這個服務(wù)器的 ip 地址;C 表示的是這個服務(wù)器與集群中的 Leader 服務(wù)器交換信息的端口;D 表示的是萬一集群中的 Leader 服務(wù)器掛了,需要一個端口來重新進行選舉,選出一個新的 Leader,而這個端口就是用來執(zhí)行選舉時服務(wù)器相互通信的端口。如果是偽集群的配置方式,由于 B 都是一樣,所以不同的 Zookeeper 實例通信端口號不能一樣,所以要給它們分配不同的端口號。
7、配置中出現(xiàn)的問題及解決方式:
1.啟動zkServer.cmd 時 cmd命令窗口閃退
這時可以修改zkServer.cmd文件內(nèi)容,在末尾加上 pause關(guān)鍵字,之后再啟動cmd窗口不會閃退,且可看到啟動時所拋出的異常,可以根據(jù)異常 描述進行相關(guān)處理,我的異常-注解中文亂碼
2.有myid還報錯Caused by: java.lang.IllegalArgumentException: myid file is missing
解決方式:將myid.txt 后綴去掉.txt,只保留myid文件名
以上兩個異常只是本人在安裝過程中遇到的問題,可能還有其他其他異常我沒遇到,下面是別人記錄的異常
3.Error: JAVA_HOME is incorrectly set:缺少jdk或者jdk版本不匹配等相關(guān)問題
4.java.net.bindexception address already:端口號被占用
5.java.lang.numberformatexception:數(shù)字轉(zhuǎn)換異常,出現(xiàn)原因可能是myid文件內(nèi)容問題,或者在使用命令啟動時在zkServer.cmd 多了其他內(nèi)容,使用命令啟動時如上圖所示選擇到zkServer.cmd即可,后面不需要任何內(nèi)容
三、Zookeeper真分布式集群(未測試)
多IP多節(jié)點:部署在不同IP,各有自己的端口(未測試)
多IP多節(jié)點:將zookeeper拷貝到每個節(jié)點一份。
多IP多節(jié)點與單IP多節(jié)點搭建過程基本一致,上述過程不再重復(fù)描述,僅重點說一個地方:server的IP地址、端口為真實即可。
注意:zk的部署個數(shù)最好為基數(shù),ZK集群的機制是只要超過半數(shù)的節(jié)點OK,集群就能正常提供服務(wù)。只有ZK節(jié)點掛得太多,只剩一半或不到一半節(jié)點能工作,集群才失效。
1.Dubbo中zookeeper做注冊中心,如果注冊中心集群都掛掉,發(fā)布者和訂閱者之間還能通信么?
啟動dubbo時,消費者會從zk拉取注冊的生產(chǎn)者的地址接口等數(shù)據(jù),緩存在本地。每次調(diào)用時,按照本地存儲的地址進行調(diào)用。但是在注冊中心全部掛掉后增加新的提供者,則不能被消費者發(fā)現(xiàn):
健狀性
- 監(jiān)控中心宕掉不影響使用,只是丟失部分采樣數(shù)據(jù)
- 數(shù)據(jù)庫宕掉后,注冊中心仍能通過緩存提供服務(wù)列表查詢,但不能注冊新服務(wù)
- 注冊中心對等集群,任意一臺宕掉后,將自動切換到另一臺
- 注冊中心全部宕掉后,服務(wù)提供者和服務(wù)消費者仍能通過本地緩存通訊
- 服務(wù)提供者無狀態(tài),任意一臺宕掉后,不影響使用
- 服務(wù)提供者全部宕掉后,服務(wù)消費者應(yīng)用將無法使用,并無限次重連等待服務(wù)提供者恢復(fù)
Dubbo默認緩存的本地路徑:C:\Users\Administrator\.dubbo
因此,就算zk服務(wù)關(guān)掉,也能繼續(xù)訪問項目。
四、驗證服務(wù)器
利用 zktools可視化 工具
工具下載地址:zktools可視化 提取碼:zv2f
使用方式:先啟動zk,再用 **zktools可視化 **連接指定的 ip:端口

Dubbo中zookeeper做注冊中心,如果注冊中心集群都掛掉,發(fā)布者和訂閱者之間還能通信么?
啟動dubbo時,消費者會從zk拉取注冊的生產(chǎn)者的地址接口等數(shù)據(jù),緩存在本地。每次調(diào)用時,按照本地存儲的地址進行調(diào)用。但是在注冊中心全部掛掉后增加新的提供者,則不能被消費者發(fā)現(xiàn):
健狀性
- 監(jiān)控中心宕掉不影響使用,只是丟失部分采樣數(shù)據(jù)
- 數(shù)據(jù)庫宕掉后,注冊中心仍能通過緩存提供服務(wù)列表查詢,但不能注冊新服務(wù)
- 注冊中心對等集群,任意一臺宕掉后,將自動切換到另一臺
- 注冊中心全部宕掉后,服務(wù)提供者和服務(wù)消費者仍能通過本地緩存通訊
- 服務(wù)提供者無狀態(tài),任意一臺宕掉后,不影響使用
- 服務(wù)提供者全部宕掉后,服務(wù)消費者應(yīng)用將無法使用,并無限次重連等待服務(wù)提供者恢復(fù)
五、springboot zookeeperk dubbo
這里只介紹怎么配置,不過多詳細介紹項目如何搭建及詳細代碼。下面調(diào)用的是二、Zookeeper 偽分布式安裝(集群) 配置的端口:2183、2184、2185
1、zk連接測試
這里只是zk測試,不需要搭建dubbo+zk分部署項目。
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>
測試集群是否搭建成功:
測試類 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);//檢測/app1是否存在
if (stat == null) {
//創(chuàng)建節(jié)點
String createResult = zk.create(node, "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(createResult);
}
//獲取節(jié)點的值
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é)點
zkClient.close();
}
}
執(zhí)行測試后,使用zktools可視化工具查看是否成功。
工具下載地址:zktools可視化 提取碼:zv2f
使用方式:先啟動zk,再用 **zktools可視化 **連接指定的 ip:端口

測試:zk端口2183:

zk端口2184:

在zk端口2185也添加節(jié)點成功,說明集群搭建成功。
2、單機測試-分布式(dubbo)
需要搭建springboot+zookeeper+dubbo分布式測試項目
web層(消費者)配置
1).web層(消費者)- 引入依賴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層(消費者)- *.yml 文件
# dubbo配置
dubbo:
application:
name: consumer # 消費方應(yīng)用名,用于計算依賴關(guān)系,不要與提供方一樣
registry:
protocol: zookeeper #dubbo/zookeeper 協(xié)議
#address: 127.0.0.1:2183 # zookeeper協(xié)議配置
address: 192.168.2.113:2183 # zookeeper協(xié)議配置 測試ip連接,zk是否支持遠程調(diào)用
interface:
version: 1.0.0 # 接口版本號
annotation:
package: com.dist # dubbo注解掃描包,注意更改成功自己項目的java文件路徑,否則注冊不到服務(wù)
consumer:
timeout: 50000 # 超時時間
check: false # check校驗:閉所有服務(wù)的啟動時檢查
version: 1.0.0 # dubbo默認版本號,在url顯示為default.version=xxx
3).web層(消費者)- 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"/>
<!--消費方應(yīng)用名,用于計算依賴關(guān)系,不是匹配條件,不要與提供方一樣-->
<dubbo:application name="${dubbo.application.name}"/>
<!--zk 注冊中心暴露服務(wù)地址,協(xié)議-->
<dubbo:registry id="zk" address="${dubbo.registry.address}" protocol="${dubbo.registry.protocol}"/>
<!--dubbo 掃描包位置-->
<dubbo:annotation package="${dubbo.annotation.package}"/>
<!--dubbo 版本,超時,check校驗:關(guān)閉所有服務(wù)的啟動時檢查-->
<dubbo:consumer version="${dubbo.consumer.version}" timeout="${dubbo.consumer.timeout}" check="${dubbo.consumer.check}"/>
</beans>
4).web層(消費者)- 需要引入api層(公共接口)依賴jar
在pom.xml添加
<!--api依賴-->
<dependency>
<groupId>com.dist</groupId>
<artifactId>springboot-test-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
5).web層(消費者)- 測試代碼:
創(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)用名,用于計算依賴關(guān)系,不要與消費方一樣
registry:
protocol: zookeeper #dubbo/zookeeper 協(xié)議
address: 127.0.0.1:2183 # zookeeper協(xié)議配置方式
#address: 192.168.2.113:2183 # zookeeper協(xié)議配置 測試ip連接,zk是否支持遠程調(diào)用
#address: zookeeper://127.0.0.1:2183 # dubbo協(xié)議配置
protocol:
port: 30103 # dubbo協(xié)議缺省port端口20880,多個提供者會沖突
annotation:
package: com.dist.server # dubbo注解掃描包,注意更改成功自己項目的java文件路徑,否則注冊不到服務(wù)
provider:
version: 1.0.0 #dubbo默認版本號,在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)用名,用于計算依賴關(guān)系,不是匹配條件,不要與消費方一樣-->
<dubbo:application name="${dubbo.application.name}"/>
<!--zk 注冊中心暴露服務(wù)地址,協(xié)議:dubbo/zookeeper,dubbo協(xié)議缺省port端口20880,多個提供者會沖突 添加 port="${dubbo.protocol.port}-->
<dubbo:registry address="${dubbo.registry.address}" protocol="${dubbo.registry.protocol}"/>
<!--dubbo 版本,超時-->
<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層(提供者)- 測試代碼:
DubboTestServiceImpl.java實現(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層實現(xiàn),需要兩者都引入api層的依賴jar:
DubboTestService.java 公共接口
public interface DubboTestService {
String getData(String data);
}
3、偽集群測試-分布式(dubbo)
在 2、單機測試-分布式(dubbo) 基礎(chǔ)上更改 *.yml 配置:
1.web層(消費者)- *.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é)議配置 測試ip連接,zk是否支持遠程調(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默認版本號,在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é)議配置 測試ip連接,zk是否支持遠程調(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默認版本號,在url顯示為default.version=xxx
到這里偽集群測試-配置完成!
備注:
web層協(xié)議和service協(xié)議最好配置一樣,protocol: dubbo/zookeeper # 協(xié)議當然兩者配置不一致也可以調(diào)用
六、Zookeeper安全認證
1、為什么Zookeeper要安全認證 ?
1).服務(wù)都是在內(nèi)網(wǎng),Zookeeper集群配置都是走的內(nèi)網(wǎng)IP,外網(wǎng)不開放相關(guān)端口,不需要zookeeper對外開放。但是可能由于業(yè)務(wù)升級,例如購置了阿里云的服務(wù),需要對外開放Zookeeper服務(wù),就需要對zookeeper進行安全認證了。
2).Zookeeper 未授權(quán)訪問(中危,3處)

2.ACL認證的簡介
首先說明一下為什么需要ACL
簡單來說 :在通常情況下,zookeeper允許未經(jīng)授權(quán)的訪問,因此在安全漏洞掃描中暴漏未授權(quán)訪問漏洞。這在一些監(jiān)控很嚴的系統(tǒng)中是不被允許的,所以需要ACL來控制權(quán)限.
既然需要ACL來控制權(quán)限,那么Zookeeper的權(quán)限有哪些呢?
權(quán)限包括以下幾種:
CREATE: 能創(chuàng)建子節(jié)點
READ:能獲取節(jié)點數(shù)據(jù)和列出其子節(jié)點
WRITE: 能設(shè)置節(jié)點數(shù)據(jù)
DELETE: 能刪除子節(jié)點
ADMIN: 能設(shè)置權(quán)限
說到權(quán)限,介紹一下zookeeper的四種認證方式:
world:默認方式,相當于全世界都能訪問
auth:代表已經(jīng)認證通過的用戶(cli中可以通過addauth digest user:pwd 來添加當前上下文中的授權(quán)用戶)
digest:即用戶名:密碼這種方式認證,這也是業(yè)務(wù)系統(tǒng)中最常用的
ip:使用Ip地址認證
ACL基本介紹就到這里。
3.沒有ACL認證時zookeeper的操作
直接上代碼 : 更改一下服務(wù)器地址和端口號即可!
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)建一個與服務(wù)器的連接
* 參數(shù)一:服務(wù)器地址和端口號(該端口號值服務(wù)器允許客戶端連接的端口號)
* 參數(shù)二:連接會話超時時間
* 參數(shù)三:觀察者,連接成功會觸發(fā)該觀察者。不過只會觸發(fā)一次。
* 該Watcher會獲取各種事件的通知
*/
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é)點的子節(jié)點
System.out.println("查看根節(jié)點的子節(jié)點:ls / => " + zk.getChildren("/", true));
System.out.println("*******************************************************");
// 創(chuàng)建一個目錄節(jié)點
if (zk.exists("/node", true) == null) {
/**
* 參數(shù)一:路徑地址
* 參數(shù)二:想要保存的數(shù)據(jù),需要轉(zhuǎn)換成字節(jié)數(shù)組
* 參數(shù)三:ACL訪問控制列表(Access control list),
* 參數(shù)類型為ArrayList<ACL>,Ids接口提供了一些默認的值可以調(diào)用。
* OPEN_ACL_UNSAFE This is a completely open ACL
* 這是一個完全開放的ACL,不安全
* CREATOR_ALL_ACL This ACL gives the
* creators authentication id's all permissions.
* 這個ACL賦予那些授權(quán)了的用戶具備權(quán)限
* READ_ACL_UNSAFE This ACL gives the world the ability to read.
* 這個ACL賦予用戶讀的權(quán)限,也就是獲取數(shù)據(jù)之類的權(quán)限。
* 參數(shù)四:創(chuàng)建的節(jié)點類型。枚舉值CreateMode
* PERSISTENT (0, false, false)
* PERSISTENT_SEQUENTIAL (2, false, true)
* 這兩個類型創(chuàng)建的都是持久型類型節(jié)點,回話結(jié)束之后不會自動刪除。
* 區(qū)別在于,第二個類型所創(chuàng)建的節(jié)點名后會有一個單調(diào)遞增的數(shù)值
* EPHEMERAL (1, true, false)
* EPHEMERAL_SEQUENTIAL (3, true, true)
* 這兩個類型所創(chuàng)建的是臨時型類型節(jié)點,在回話結(jié)束之后,自動刪除。
* 區(qū)別在于,第二個類型所創(chuàng)建的臨時型節(jié)點名后面會有一個單調(diào)遞增的數(shù)值。
* 最后create()方法的返回值是創(chuàng)建的節(jié)點的實際路徑
*/
zk.create("/node", "conan".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("創(chuàng)建一個目錄節(jié)點:create /node conan");
/**
* 查看/node節(jié)點數(shù)據(jù),這里應(yīng)該輸出"conan"
* 參數(shù)一:獲取節(jié)點的路徑
* 參數(shù)二:說明是否需要觀察該節(jié)點,設(shè)置為true,則設(shè)定共享默認的觀察器
* 參數(shù)三:stat類,保存節(jié)點的信息。例如數(shù)據(jù)版本信息,創(chuàng)建時間,修改時間等信息
*/
System.out.println("查看/node節(jié)點數(shù)據(jù):get /node => "
+ new String(zk.getData("/node", false, null)));
/**
* 查看根節(jié)點
* 在此查看根節(jié)點的值,這里應(yīng)該輸出上面所創(chuàng)建的/node節(jié)點
*/
System.out.println("查看根節(jié)點:ls / => " + zk.getChildren("/", true));
}
System.out.println("*******************************************************");
// 創(chuàng)建一個子目錄節(jié)點
if (zk.exists("/node/sub1", true) == null) {
zk.create("/node/sub1", "sub1".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("創(chuàng)建一個子目錄節(jié)點:create /node/sub1 sub1");
// 查看node節(jié)點
System.out.println("查看node節(jié)點:ls /node => "
+ zk.getChildren("/node", true));
}
System.out.println("*******************************************************");
/**
* 修改節(jié)點數(shù)據(jù)
* 修改的數(shù)據(jù)會覆蓋上次所設(shè)置的數(shù)據(jù)
* setData()方法參數(shù)一、參數(shù)二不多說,與上面類似。
* 參數(shù)三:數(shù)值型。需要傳入該界面的數(shù)值類型版本號?。。? * 該信息可以通過Stat類獲取,也可以通過命令行獲取。
* 如果該值設(shè)置為-1,就是忽視版本匹配,直接設(shè)置節(jié)點保存的值。
*/
if (zk.exists("/node", true) != null) {
zk.setData("/node", "changed".getBytes(), -1);
// 查看/node節(jié)點數(shù)據(jù)
System.out.println("修改節(jié)點數(shù)據(jù):get /node => "
+ new String(zk.getData("/node", false, null)));
}
System.out.println("*******************************************************");
// 刪除節(jié)點
if (zk.exists("/node/sub1", true) != null) {
zk.delete("/node/sub1", -1);
zk.delete("/node", -1);
// 查看根節(jié)點
System.out.println("刪除節(jié)點:ls / => " + zk.getChildren("/", true));
}
// 關(guān)閉連接
zk.close();
}
}
認證只是針對一個節(jié)點
認證只是針對一個節(jié)點
ACL【Access Control List】,ZooKeeper作為一個分布式協(xié)調(diào)框架,其內(nèi)部存儲的都是一些關(guān)乎分布式系統(tǒng)運行時狀態(tài)的元數(shù)據(jù),尤其是涉及到一些分布式鎖,Master選舉和協(xié)調(diào)等應(yīng)用場景。我們需要有效的保障ZooKeeper中的數(shù)據(jù)安全,ZooKeeper提供了三種模式。權(quán)限模式、授權(quán)對象、權(quán)限。
權(quán)限模式:Scheme,開發(fā)人員最多使用的如下四種權(quán)限模式:
IP:IP模式通過IP地址粒度來進行控制權(quán)限,例如配置了:IP,192.168.1.107即表示權(quán)限控制都是針對這個IP地址的,同時也支持按網(wǎng)段分配,比如:192.168.1.*Digest:digest是最常用的權(quán)限控制模式,也更符合我們對權(quán)限控制的認識,其類似于“username:password”形式的權(quán)限標識進行權(quán)限配置。ZooKeeper會對形式的權(quán)限標識先后進行兩次編碼處理,分別是SHA-1加密算法,BASE64編碼
World:World是一直最開放的權(quán)限控制模式。這種控制模式可以看做為特殊的Digest,它僅僅是一個標識而已
Super:超級用戶模式,在超級用戶模式下可以對ZooKeeper任意進行操作
授權(quán)對象:指的是權(quán)限賦予的用戶或者一個指定的實體,例如IP地址或者機器等。在不同的模式下,授權(quán)對象是不同的。這種模式和權(quán)限對象一一對應(yīng)
權(quán)限:權(quán)限就是指那些通過權(quán)限檢測后可以被允許執(zhí)行的操作,在ZooKeeper中,對數(shù)據(jù)的操作權(quán)限分為以下五個大類:CREATE、DELETE、READ、WRITE、ADMIN
4.有ACL認證時zookeeper的操作測試:
pom.xml
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
<scope>test</scope>
</dependency>
代碼類:Zookeeper 節(jié)點授權(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é)點授權(quán)
* 這里測試的認證方式:digest 相當于 user:pass 認證
*
* @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";
/** 測試路徑 */
final static String PATH = "/testAuth";
final static String PATH_DEL = "/testAuth/delNode";
/** 認證類型 */
final static String authentication_type = "digest";
/** 認證正確方法 */
final static String correctAuthentication = "123456";
/** 認證錯誤方法 */
final static String badAuthentication = "654321";
static ZooKeeper zk = null;
/** 計時器 */
AtomicInteger seq = new AtomicInteger();
/** 標識 */
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 + "會話失效");
}
System.out.println("--------------------------------------------");
}
/**
* 創(chuàng)建ZK連接
*
* @param connectString
* ZK服務(wù)器地址列表
* @param sessionTimeout
* Session超時時間
*/
public void createConnection(String connectString, int sessionTimeout) {
this.releaseConnection();
try {
zk = new ZooKeeper(connectString, sessionTimeout, this);
//添加節(jié)點授權(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>測試函數(shù)<BR>
* <B>概要說明:</B>測試認證<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é)點:"+ 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é)點:"+ PATH_DEL + ", 初始內(nèi)容是: will be deleted!");
} catch (Exception e) {
e.printStackTrace();
}
// 獲取數(shù)據(jù)
getDataByNoAuthentication(); //獲取數(shù)據(jù):不采用密碼
getDataByBadAuthentication(); //獲取數(shù)據(jù):采用錯誤的密碼
getDataByCorrectAuthentication(); //采用正確的密碼
// 更新數(shù)據(jù)
updateDataByNoAuthentication(); //更新數(shù)據(jù):不采用密碼
updateDataByBadAuthentication(); //更新數(shù)據(jù):采用錯誤的密碼
updateDataByCorrectAuthentication(); //更新數(shù)據(jù):采用正確的密碼
// 刪除數(shù)據(jù)
deleteNodeByNoAuthentication(); //不使用密碼 刪除節(jié)點
deleteNodeByBadAuthentication(); //采用錯誤的密碼刪除節(jié)點
deleteNodeByCorrectAuthentication(); //使用正確的密碼刪除節(jié)點
//線程等待
Thread.sleep(1000);
//使用正確的密碼刪除父節(jié)點
deleteParent();
//釋放連接
testAuth.releaseConnection();
}
/** 獲取數(shù)據(jù):采用錯誤的密碼 */
static void getDataByBadAuthentication() {
String prefix = "[使用錯誤的授權(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ù):采用錯誤的密碼
*/
static void updateDataByBadAuthentication() {
String prefix = "[使用錯誤的授權(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é)點
*/
static void deleteNodeByNoAuthentication() throws Exception {
String prefix = "[不使用任何授權(quán)信息]";
try {
System.out.println(prefix + "刪除節(jié)點:" + 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());
}
}
/**
* 采用錯誤的密碼刪除節(jié)點
*/
static void deleteNodeByBadAuthentication() throws Exception {
String prefix = "[使用錯誤的授權(quán)信息]";
try {
System.out.println(prefix + "刪除節(jié)點:" + 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é)點
*/
static void deleteNodeByCorrectAuthentication() throws Exception {
String prefix = "[使用正確的授權(quán)信息]";
try {
System.out.println(prefix + "刪除節(jié)點:" + 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é)點
*/
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é)點失敗,原因是:" + e.getMessage());
e.printStackTrace();
}
}
}
5.zookeeper超級用戶配置(Windows/Linux)
七、Zookeeper+Dubbo認證
- 可通過 < dubbo:registry username="admin" password="1234" /> 設(shè)置 zookeeper 登錄信息
- 可通過 < dubbo:registry group="dubbo" /> 設(shè)置 zookeeper 的根節(jié)點,不設(shè)置將使用無根樹
官網(wǎng)文檔第五條,明確說明了可以通過username和 password字段設(shè)置zookeeper 登錄信息。
但是,如果在Zookeeper上通過digest方式設(shè)置ACL,然后在dubbo registry上配置相應(yīng)的用戶、密碼,服務(wù)就注冊不到Zookeeper上了,會報KeeperErrorCode = NoAuth錯誤。
看了下調(diào)用相關(guān)代碼,發(fā)現(xiàn)注冊服務(wù)時所傳的ACL,而配置在dubbo上的,沒有發(fā)現(xiàn)被使用的地方(如果注冊中心是Zookeeper的話)。
但是查閱ZookeeperRegistry相關(guān)源碼并沒有發(fā)現(xiàn)相關(guān)認證的地方,搜遍全網(wǎng)很少有問類似的問題,這個問題似乎并沒有多少人關(guān)注。
大部分服務(wù)大都是部署在內(nèi)網(wǎng)的,基本很少對外網(wǎng)開放,然而Dubbo的zookeeper用戶權(quán)限認證貌似真的不起作用,如果非要對外開放只能通過iptables或者firewall進行IP Access Control,如果是阿里云服務(wù)器的話安全組也是個不錯的選擇