1 方法遠程調(diào)用代碼的對比
// 使用遠程方法調(diào)用框架
@Controller
public class HelloController {
// 將封裝了遠程服務(wù)功能的接口類型的bean裝配到當前組件
@Autowired
private EmpRemoteService empRemoteService;
@RequestMapping("/aaa/bbb/ccc")
public String doXxx(){
// 像調(diào)用本地方法一樣調(diào)用遠程方法
List<Emp> list = empRemoteService.getEmpList();
// ...
}
}
// 不使用遠程方法調(diào)用框架
// 1.創(chuàng)建HttpClient實例
CloseableHttpClient client = HttpClientBuilder.create().build();
try {
// 2.聲明服務(wù)器端URL地址
String url = "http://[服務(wù)器端實際IP]:54321/bookManager/book/getBook/23?requestParam=AAA";
// 3.創(chuàng)建具體請求方式實例
HttpGet get = new HttpGet(url);
// 4.調(diào)用客戶端對象執(zhí)行請求,進而獲得響應(yīng)對象
CloseableHttpResponse response = client.execute(get);
// 5.從響應(yīng)結(jié)果中獲取封裝響應(yīng)數(shù)據(jù)的Entity對象
HttpEntity entity = response.getEntity();
// 6.借助工具類將HttpEntity中包含的數(shù)據(jù)轉(zhuǎn)換成可識別的字符串
String responseData = EntityUtils.toString(entity, "UTF-8");
// 7.處理響應(yīng)結(jié)果
System.out.println(responseData);
//Ensures that the entity content is fully consumed and the content stream, if exists, is closed.
//確認entity的內(nèi)容已經(jīng)完全被consume了,而且如果內(nèi)容的流存在缠诅,確認其已經(jīng)關(guān)閉了枣抱。
EntityUtils.consume(entity);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8.釋放連接,無論操作是否成功都必須釋放連接
if(client != null) {try {client.close();} catch (IOException e) {e.printStackTrace();}}
}
2. 遠程方法調(diào)用框架和注冊中心工作機制
2.1 技術(shù)方案一
Dubbo作為遠程方法調(diào)用框架+Zookeeper作為注冊中心
2.2 技術(shù)方案二
SpringBoot+SpringCloud
→Eureka作為注冊中心
→Feign作為遠程方法調(diào)用框架
3. Zookeeper
3.1 簡介
ZooKeeper是用于維護配置信息,命名,提供分布式同步和提供組服務(wù)的集中式服務(wù)。所有這些類型的服務(wù)都以某種形式或由分布式應(yīng)用程序使用灸拍。每次實施它們時,都會進行很多工作來修復(fù)不可避免的錯誤和競爭條件砾省。
由于難以實現(xiàn)這類服務(wù)鸡岗,因此應(yīng)用程序通常最初會在其上跳過,從而使它們在存在更改的情況下變得脆弱并且難以管理编兄。即使部署正確轩性,當部署應(yīng)用程序時,這些服務(wù)的不同實現(xiàn)也會導(dǎo)致管理復(fù)雜狠鸳。
3.2 樹形目錄結(jié)構(gòu)
ZooKeeper使用樹形結(jié)構(gòu)管理數(shù)據(jù)揣苏。而且以“/”作為樹形結(jié)構(gòu)的根節(jié)點悯嗓。樹形結(jié)構(gòu)中的每一個節(jié)點都稱為“znode”。文件系統(tǒng)中的目錄可以存放其他目錄和文件卸察,znode中可以存放其他znode脯厨,也可以對應(yīng)一個具體的值。znode和它對應(yīng)的值之間是鍵值對的關(guān)系坑质。
每一個znode上同時還有一套狀態(tài)信息合武,稱為:stat。
3.3 異步通知機制
在分布式項目中隨著業(yè)務(wù)功能越來越多涡扼,具體的功能模塊也會越來越多稼跳,一個大型的電商項目能達到幾十個模塊甚至更多。這么多業(yè)務(wù)模塊的工程有可能需要共享一些信息吃沪,這些信息一旦發(fā)生變化岂贩,在各個相關(guān)模塊工程中手動逐一修改會非常麻煩,甚至可能發(fā)生遺漏巷波,嚴重的可能導(dǎo)致系統(tǒng)崩潰,造成經(jīng)濟損失卸伞。
使用ZooKeeper的通知機制后抹镊,各個模塊工程在特定znode上設(shè)置Watcher(觀察者)來監(jiān)控當前節(jié)點上值的變化。一旦Watcher檢測到了數(shù)據(jù)變化就會立即通知模塊工程荤傲,從而自動實現(xiàn)“一處修改垮耳,處處生效”的效果。
3.4 leader-follower集群
4. Zookeeper安裝
4.1 環(huán)境準備
Zookeeper需要在JVM虛擬機上運行遂黍,所以一定要保證有JDK支持终佛。
- 上傳JDK到linux虛擬機
- 到該路徑下
- 解壓jdk
tar -zxvf jdk-8u231-linux-x64.tar.gz
-
編輯etc/profile文件
在文件末尾加上
JAVA_HOME=/opt/jdk1.8.0_231 PATH=$JAVA_HOME/bin:$PATH export JAVA_HOME PATH
-
接著進入到j(luò)dk文件中輸入命令
即JKD環(huán)境搭建成功
4.2 上傳Zookeeper
zookeeper-3.4.9.tar.gz
4.3 解壓
tar -zxvf /opt/zookeeper-3.4.9.tar.gz
4.4 準備配置文件
cp /opt/zookeeper-3.4.9/conf/zoo_sample.cfg /opt/zookeeper-3.4.9/conf/zoo.cfg
Zookeeper要求配置文件的文件名必須是:zoo.cfg
4.5 創(chuàng)建數(shù)據(jù)目錄
mkdir /opt/zookeeper-3.4.9/data
4.6 在zoo.cfg中配置數(shù)據(jù)目錄的位置
dataDir=/opt/zookeeper-3.4.9/data
5 Zookeeper服務(wù)器端操作
5.1 zoo.cfg文件解讀
tickTime
通信心跳數(shù),ZooKeeper服務(wù)器心跳時間,單位毫秒
ZooKeeper使用的基本時間雾家,服務(wù)器之間或客戶端與服務(wù)器之間維持心跳的時間間隔铃彰,也就是每個tickTime時間就會發(fā)送一個心跳,時間單位為毫秒芯咧。
用于心跳機制牙捉,并且設(shè)置最小的session超時時間為兩倍心跳時間(session的最小超時時間是2*tickTime)。
initLimit
LF初始通信時限
集群中的Follower跟隨者服務(wù)器(F)與Leader領(lǐng)導(dǎo)者服務(wù)器(L)之間初始連接時能容忍的最多心跳數(shù)(tickTime的數(shù)量)敬飒。
投票選舉新Leader的初始化時間邪铲,F(xiàn)ollower在啟動過程中,會從Leader同步所有最新數(shù)據(jù)无拗,然后確定自己能夠?qū)ν夥?wù)的起始狀態(tài)带到。
Leader允許Follower在initLimit時間內(nèi)完成這個工作。
syncLimit
LF同步通信時限
集群中Leader與Follower之間的最大響應(yīng)時間單位英染,假如響應(yīng)超過syncLimit * tickTime揽惹,Leader認為Follwer死掉被饿,從服務(wù)器列表中刪除Follwer。
在運行過程中永丝,Leader負責與ZooKeeper集群中所有機器進行通信锹漱,例如通過一些心跳檢測機制,來檢測機器的存活狀態(tài)慕嚷。
如果L發(fā)出心跳包在syncLimit之后哥牍,還沒有從F那收到響應(yīng),那么就認為這個F已經(jīng)不在線了喝检。
dataDir
數(shù)據(jù)文件目錄+數(shù)據(jù)持久化路徑
保存內(nèi)存數(shù)據(jù)庫快照信息的位置嗅辣,如果沒有其他說明,更新的事務(wù)日志也保存到數(shù)據(jù)庫挠说。
clientPort
客戶端連接端口
5.2 常用命令
5.2.1 ZooKeeper服務(wù)器與客戶端
- 啟動服務(wù)器:
./zkServer.sh start
- 停止服務(wù)器:
./zkServer.sh stop
- 啟動客戶端:
./zkCli.sh
- 退出客戶端:
[zk: localhost:2181(CONNECTED) 6] quit
5.2.2 ls
查看當前znode中所包含的內(nèi)容
5.2.3 ls2
查看當前節(jié)點數(shù)據(jù)并能看到更新次數(shù)等數(shù)據(jù)
5.2.4 stat
查看節(jié)點狀態(tài)
5.2.5 create
create [-s] [-e] path data acl
- 普通創(chuàng)建:不帶有-s澡谭、-e參數(shù)
- -s:含有序列
- -e:臨時(重啟或者超時消失)
5.2.6 set
設(shè)置節(jié)點的具體值
set 節(jié)點 value值
5.2.7 get
獲得節(jié)點的值
get節(jié)點
5.2.8 delete
可以刪除指定znode,當該znode擁有子znode時损俭,必須先刪除其所有子znode蛙奖,否則操作將失敗。
5.2.9 rmr
rmr命令可用于代替delete命令杆兵,rmr是一個遞歸刪除命令雁仲,如果發(fā)生指定節(jié)點擁有子節(jié)點時,rmr命令會首先刪除子節(jié)點琐脏。
5.3 Zookeeper節(jié)點類型
-
PERSISTENT-持久化目錄節(jié)點
客戶端與zookeeper斷開連接后攒砖,該節(jié)點依舊存在
-
PERSISTENT_SEQUENTIAL-持久化順序編號目錄節(jié)點
客戶端與zookeeper斷開連接后,該節(jié)點依舊存在日裙,只是Zookeeper給該節(jié)點名稱進行順序編號
-
EPHEMERAL-臨時目錄節(jié)點
客戶端與zookeeper斷開連接后吹艇,該節(jié)點被刪除
-
EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節(jié)點
客戶端與zookeeper斷開連接后,該節(jié)點被刪除昂拂,只是Zookeeper給該節(jié)點名稱進行順序編號
5.4 Zookeeper節(jié)點狀態(tài)
5.4.1 介紹
znode維護了一個stat結(jié)構(gòu)受神,這個stat包含數(shù)據(jù)變化的版本號、訪問控制列表變化政钟、還有時間戳路克。版本號和時間戳一起,可讓ZooKeeper驗證緩存和協(xié)調(diào)更新养交。每次znode的數(shù)據(jù)發(fā)生了變化精算,版本號就增加。
例如:無論何時客戶端檢索數(shù)據(jù)碎连,它也一起檢索數(shù)據(jù)的版本號灰羽。并且當客戶端執(zhí)行更新或刪除時,客戶端必須提供他正在改變的znode的版本號。如果它提供的版本號和真實的數(shù)據(jù)版本號不一致廉嚼,更新將會失敗玫镐。
5.4.2 屬性
czxid:引起這個znode創(chuàng)建的zxid,創(chuàng)建節(jié)點的事務(wù)的zxid(ZooKeeper Transaction Id)
ctime:znode被創(chuàng)建的毫秒數(shù)(從1970年開始)
mzxid:znode最后更新的zxid
mtime:znode最后修改的毫秒數(shù)(從1970年開始)
pZxid:znode最后更新的子節(jié)點zxid
cversion:znode子節(jié)點變化號怠噪,znode子節(jié)點修改次數(shù)
dataversion:znode數(shù)據(jù)變化號
aclVersion:znode訪問控制列表的變化號
ephemeralOwner:如果是臨時節(jié)點恐似,這個是znode擁有者的session id。如果不是臨時節(jié)點則是0傍念。
dataLength:znode的數(shù)據(jù)長度
-
numChildren:znode子節(jié)點數(shù)量
5.5 四字命令
5.5.1 介紹
ZooKeeper支持某些特定的四字命令矫夷,他們大多是用來查詢ZooKeeper服務(wù)的當前狀態(tài)及相關(guān)信息的,使用時通過telnet或nc向ZooKeeper提交相應(yīng)命令憋槐。
[root@right bin]# echo ruok | nc localhost 2181
imok[root@right bin]#
5.5.2 nc命令
nc命令需要安裝對應(yīng)的程序才可以使用双藕。
yum install -y nc
5.5.3 常用四字命令
- ruok:測試服務(wù)是否處于正確狀態(tài)。如果確實如此阳仔,那么服務(wù)返回“imok ”忧陪,否則不做任何響應(yīng)
- stat:輸出關(guān)于性能和連接的客戶端的列表
- conf:輸出相關(guān)服務(wù)配置的詳細信息
- cons:列出所有連接到服務(wù)器的客戶端的完全的連接 /會話的詳細信息。包括“接受 / 發(fā)送”的包數(shù)量近范、會話id 嘶摊、操作延遲、最后的操作執(zhí)行等等信息
- dump:列出未經(jīng)處理的會話和臨時節(jié)點
- envi:輸出關(guān)于服務(wù)環(huán)境的詳細信息(區(qū)別于conf命令)
- reqs:列出未經(jīng)處理的請求
- wchs:列出服務(wù)器watch的詳細信息
- wchc:通過session列出服務(wù)器watch的詳細信息评矩,它的輸出是一個與watch相關(guān)的會話的列表
- wchp:通過路徑列出服務(wù)器 watch的詳細信息更卒。它輸出一個與 session相關(guān)的路徑
6 Java客戶端
6.1 依賴信息
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
6.2 修改節(jié)點數(shù)據(jù)操作
public class ZkTest {
private ZooKeeper zooKeeper;
{
String connectString = "192.168.56.150:2181";
int sessionTimeout = 5000;
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {}
};
try {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, watcher);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testUpdateNodeData() throws KeeperException, InterruptedException {
// 要操作的節(jié)點的路徑
String path = "/animal/cat";
// 獲取節(jié)點當前值
byte[] resultByteArray = zooKeeper.getData(path, false, new Stat());
// 將字節(jié)數(shù)組封裝為字符串
String result = new String(resultByteArray);
// 打印舊值
System.out.println("old value="+result);
// 獲取新值字符串對應(yīng)的字節(jié)數(shù)組
byte[] newValueByteArray = new String("miaomiao").getBytes();
// 指定當前操作所基于的版本號,如果不確定可以使用-1
int version = -1;
// 執(zhí)行節(jié)點值的修改
Stat stat = zooKeeper.setData(path, newValueByteArray, version);
// 獲取最新版本號
int newVersion = stat.getVersion();
System.out.println("newVersion="+newVersion);
// 獲取節(jié)點新值
resultByteArray = zooKeeper.getData(path, false, new Stat());
result = new String(resultByteArray);
System.out.println("new value="+result);
}
}
7. 異步通知機制
7.1 工作機制介紹
客戶端注冊監(jiān)聽它關(guān)心的目錄節(jié)點稚照,當目錄節(jié)點發(fā)生變化(數(shù)據(jù)改變、被刪除俯萌、子目錄節(jié)點增加刪除)時果录,zookeeper會通知客戶端。
ZooKeeper支持Watch(觀察)機制咐熙,客戶端可以在每個znode結(jié)點上設(shè)置一個Watcher(觀察者)弱恒。如果被觀察服務(wù)端的znode結(jié)點有變更,那么Watcher就會被觸發(fā)棋恼,這個Watcher所屬的客戶端將接收到一個通知包被告知結(jié)點已經(jīng)發(fā)生變化返弹,這就是把相應(yīng)的事件通知給設(shè)置過Watcher的Client端。
ZooKeeper里的所有讀取操作:getData(),getChildren()和exists()都有設(shè)置Watch的選項爪飘。
總結(jié)成一句話:ZooKeeper的觀察機制是一種異步回調(diào)的觸發(fā)機制义起。
當數(shù)據(jù)有了變化時zkServer向客戶端發(fā)送一個Watch通知,這是個一次性動作师崎,觸發(fā)一次就失效了默终。
如果想繼續(xù)Watch的話,需要客戶端重新設(shè)置Watcher。因此如果你得到了一個Watch事件齐蔽,并且在將來繼續(xù)得到節(jié)點變化通知两疚,那么就必須另外設(shè)置一個新的Watcher繼續(xù)觀察。
節(jié)點有不同的改動方式含滴∮詹常可以認為ZooKeeper維護兩個觀察列表:數(shù)據(jù)觀察和子節(jié)點觀察。getData()和exists()設(shè)置數(shù)據(jù)觀察谈况。getChildren()設(shè)置子節(jié)點觀察勺美。此外,還可以認為不同的返回數(shù)據(jù)有不同的觀察鸦做。getData()和exists()返回節(jié)點的數(shù)據(jù)励烦,而getChildren()返回子節(jié)點列表。所以泼诱,setData()將為znode觸發(fā)數(shù)據(jù)觀察坛掠。成功的create()將為新創(chuàng)建的節(jié)點觸發(fā)數(shù)據(jù)觀察,為其父節(jié)點觸發(fā)子節(jié)點觀察治筒。成功的delete()將會為被刪除的節(jié)點觸發(fā)數(shù)據(jù)觀察以及子節(jié)點觀察(因為節(jié)點不能再有子節(jié)點了)屉栓,為其父節(jié)點觸發(fā)子節(jié)點觀察。如果一個節(jié)點設(shè)置存在觀察時尚未創(chuàng)建耸袜,并且在斷開連接后執(zhí)行節(jié)點創(chuàng)建以及刪除操作友多,那么這個節(jié)點上設(shè)置的觀察事件客戶端接收不到,事件會丟失堤框。
7.2 一次性通知
@Test
public void testNoticeOnce() throws KeeperException, InterruptedException {
String path = "/animal/cat";
Watcher watcher = new Watcher() {
@Override
// 當前Watcher檢測到節(jié)點值的修改域滥,會調(diào)用這個process()方法
public void process(WatchedEvent event) {
System.err.println("接收到了通知!值發(fā)生了修改蜈抓!");
}
};
byte[] oldValue = zooKeeper.getData(path, watcher, new Stat());
System.out.println("old value="+new String(oldValue));
while(true) {
Thread.sleep(5000);
System.err.println("當前方法原本要執(zhí)行的業(yè)務(wù)邏輯");
}
}
7.3 持續(xù)通知
@Test
public void testNoticeForever() throws KeeperException, InterruptedException {
String path = "/animal/cat";
getDataWithNotice(zooKeeper, path);
while(true) {
Thread.sleep(5000);
System.err.println("當前方法原本要執(zhí)行的業(yè)務(wù)邏輯 線程名稱:"+Thread.currentThread().getName());
}
}
public void getDataWithNotice(ZooKeeper zooKeeper, String path) throws KeeperException, InterruptedException {
byte[] resultByteArray = zooKeeper.getData(path, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 以類似遞歸的方式調(diào)用getDataWithNotice()方法實現(xiàn)持續(xù)監(jiān)控
try {
getDataWithNotice(zooKeeper, path);
System.err.println("通知 線程名稱:"+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Stat());
String result = new String(resultByteArray);
System.err.println("當前節(jié)點值="+result);
}
8 集群
8.1 數(shù)據(jù)通信機制
- 同一臺服務(wù)器上:IP地址一樣启绰,端口號必須得不一樣
- 不同的服務(wù)器上:IP地址不一樣,端口號可以用同一個
8.2 搭建步驟
第1步:創(chuàng)建集群所在目錄
mkdir /opt/cluster-zk
第2步:重新解壓tar包到集群目錄
tar -zxvf /opt/zookeeper-3.4.9.tar.gz -C /opt/cluster-zk/
第3步:復(fù)制新的解壓目錄
cp -r /opt/cluster-zk/zookeeper-3.4.9/ /opt/cluster-zk/zkone
第4步:配置zkone
i:創(chuàng)建zoo.cfg配置文件
cp /opt/cluster-zk/zkone/conf/zoo_sample.cfg /opt/cluster-zk/zkone/conf/zoo.cfg
ii:創(chuàng)建數(shù)據(jù)目錄
mkdir /opt/cluster-zk/zkone/data
iii:在數(shù)據(jù)目錄中創(chuàng)建編號文件
vim /opt/cluster-zk/zkone/data/myid
touch myid
iv:編輯編號文件沟使,內(nèi)容就是當前服務(wù)器實例的編號
1
v:配置zoo.cfg
dataDir=/opt/cluster-zk/zkone/data
clientPort=1000
server.1=127.0.0.1:1001:1002
server.2=127.0.0.1:2001:2002
server.3=127.0.0.1:3001:3002
格式解釋:server.服務(wù)器實例編號=IP地址:數(shù)據(jù)通信端口號:選舉端口號
第5步:配置zktwo
i:把zktwo復(fù)制出來
cp -r /opt/cluster-zk/zkone /opt/cluster-zk/zktwo
ii:修改myid文件中編號值
vim /opt/cluster-zk/zktwo/data/myid
將1改成2
iii:修改zoo.cfg
dataDir=/opt/cluster-zk/zktwo/data
clientPort=2000
第6步:配置zkthree
i:把zkthree復(fù)制出來
cp -r /opt/cluster-zk/zkone /opt/cluster-zk/zkthree
ii:修改myid文件中編號值
vim /opt/cluster-zk/zkthree/data/myid
將1改成3
iii:修改zoo.cfg
dataDir=/opt/cluster-zk/zkthree/data
clientPort=3000
8.3 創(chuàng)建操作服務(wù)器的可執(zhí)行腳本
8.3.1 創(chuàng)建文件
- /opt/cluster-zk/start.sh
- /opt/cluster-zk/stop.sh
- /opt/cluster-zk/status.sh
8.3.2 編輯啟動服務(wù)器命令
- /opt/cluster-zk/zkone/bin/zkServer.sh start
- /opt/cluster-zk/zktwo/bin/zkServer.sh start
- /opt/cluster-zk/zkthree/bin/zkServer.sh start
8.3.3 編輯停止服務(wù)器命令
- /opt/cluster-zk/zkone/bin/zkServer.sh stop
- /opt/cluster-zk/zktwo/bin/zkServer.sh stop
- /opt/cluster-zk/zkthree/bin/zkServer.sh stop
8.3.4 編輯查看服務(wù)器狀態(tài)命令
- /opt/cluster-zk/zkone/bin/zkServer.sh status
- /opt/cluster-zk/zktwo/bin/zkServer.sh status
- /opt/cluster-zk/zkthree/bin/zkServer.sh status
8.3.5 給腳本文件設(shè)置可執(zhí)行權(quán)限
- chmod 755 /opt/cluster-zk/start.sh
- chmod 755 /opt/cluster-zk/stop.sh
- chmod 755 /opt/cluster-zk/status.sh
8.3.6 執(zhí)行腳本
- /opt/cluster-zk/start.sh
- /opt/cluster-zk/stop.sh
- /opt/cluster-zk/status.sh
8.3.7 客戶端登錄
- /opt/zookeeper-3.4.9/bin/zkCli.sh -server 127.0.0.1:1000
- /opt/zookeeper-3.4.9/bin/zkCli.sh -server 127.0.0.1:2000
- /opt/zookeeper-3.4.9/bin/zkCli.sh -server 127.0.0.1:3000
8.4 集群中服務(wù)器數(shù)量
結(jié)論:一般來說委可,集群中服務(wù)器數(shù)量最好設(shè)置為單數(shù)。
原則:集群中有超過一半的服務(wù)器正常工程腊嗡,則整個集群判斷為正常工作着倾。對外提供服務(wù)時大致滿足預(yù)期。
-
推導(dǎo):
共2實例:宕機1實例燕少,剩下1=2/2卡者,沒有超過一半。死亡容忍度為0客们。
共3實例:宕機1實例虎眨,剩下2>3/2蟋软,超過一半。死亡容忍度為1嗽桩。
共4實例:宕機1實例岳守,剩下3>4/2,超過一半碌冶。死亡容忍度為1湿痢。
共5實例:宕機2實例,剩下3>5/2扑庞,超過一半譬重。死亡容忍度為2。
共6實例:宕機2實例罐氨,剩下4>6/2臀规,超過一半。死亡容忍度為2栅隐。