一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

不懂 ZooKeeper?沒(méi)關(guān)系,這一篇給你講的明明白白

 年少一夢(mèng)bnokq3 2020-07-04

本來(lái)想系統(tǒng)回顧下 ZooKeeper的,可是網(wǎng)上沒(méi)找到一篇合自己胃口的文章,寫(xiě)的差不多的,感覺(jué)大部分都是基于《從Paxos到ZooKeeper 分布式一致性原理與實(shí)踐》寫(xiě)的,所以自己讀了一遍,加上項(xiàng)目中的使用,做個(gè)整理。加油,奧利給!

前言

面試常常被要求「熟悉分布式技術(shù)」,當(dāng)年搞 “XXX管理系統(tǒng)” 的時(shí)候,我都不知道分布式系統(tǒng)是個(gè)啥。分布式系統(tǒng)是一個(gè)硬件或軟件組件分布在不同的網(wǎng)絡(luò)計(jì)算機(jī)中上,彼此之間僅僅通過(guò)消息傳遞進(jìn)行通信和協(xié)調(diào)的系統(tǒng)。

計(jì)算機(jī)系統(tǒng)從集中式到分布式的變革伴隨著包括分布式網(wǎng)絡(luò)、分布式事務(wù)、分布式數(shù)據(jù)一致性等在內(nèi)的一系列問(wèn)題和挑戰(zhàn),同時(shí)也催生了一大批諸如ACID、CAPBASE 等經(jīng)典理論的快速發(fā)展。

為了解決分布式一致性問(wèn)題,涌現(xiàn)出了一大批經(jīng)典的一致性協(xié)議和算法,最為著名的就是二階段提交協(xié)議(2PC),三階段提交協(xié)議(3PC)和Paxos算法。Zookeeper的一致性是通過(guò)基于 Paxos 算法的 ZAB 協(xié)議完成的。一致性協(xié)議之前的文章也有介紹:「走進(jìn)分布式一致性協(xié)議」從2PC、3PC、Paxos 到 ZAB,這里就不再說(shuō)了。

1. 概述

1.1 定義

ZooKeeper 官網(wǎng)是這么介紹的:”Apache ZooKeeper 致力于開(kāi)發(fā)和維護(hù)一個(gè)支持高度可靠的分布式協(xié)調(diào)的開(kāi)源服務(wù)器

1.2 ZooKeeper是個(gè)啥

ZooKeeper 是 Apache 軟件基金會(huì)的一個(gè)軟件項(xiàng)目,它為大型「分布式計(jì)算」提供開(kāi)源的分布式配置服務(wù)、同步服務(wù)和命名注冊(cè)。

Zookeeper 最早起源于雅虎研究院的一個(gè)研究小組。在當(dāng)時(shí),研究人員發(fā)現(xiàn),在雅虎內(nèi)部很多大型系統(tǒng)基本都需要依賴一個(gè)類似的系統(tǒng)來(lái)進(jìn)行分布式協(xié)調(diào),但是這些系統(tǒng)往往都存在分布式單點(diǎn)問(wèn)題。所以,雅虎的開(kāi)發(fā)人員就試圖開(kāi)發(fā)一個(gè)通用的無(wú)單點(diǎn)問(wèn)題的分布式協(xié)調(diào)框架,以便讓開(kāi)發(fā)人員將精力集中在處理業(yè)務(wù)邏輯上,Zookeeper 就這樣誕生了。后來(lái)捐贈(zèng)給了 Apache ,現(xiàn)已成為 Apache 頂級(jí)項(xiàng)目。

關(guān)于“ZooKeeper”這個(gè)項(xiàng)目的名字,其實(shí)也有一段趣聞。在立項(xiàng)初期,考慮到之前內(nèi)部很多項(xiàng)目都是使用動(dòng)物的名字來(lái)命名的(例如著名的Pig項(xiàng)目),雅虎的工程師希望給這個(gè)項(xiàng)目也取一個(gè)動(dòng)物的名字。時(shí)任研究院的首席科學(xué)家 RaghuRamakrishnan 開(kāi)玩笑地說(shuō):“再這樣下去,我們這兒就變成動(dòng)物園了!”此話一出,大家紛紛表示就叫動(dòng)物園管理員吧一一一因?yàn)楦鱾€(gè)以動(dòng)物命名的分布式組件放在一起,雅虎的整個(gè)分布式系統(tǒng)看上去就像一個(gè)大型的動(dòng)物園了,而 Zookeeper 正好要用來(lái)進(jìn)行分布式環(huán)境的協(xié)調(diào)一一于是,Zookeeper 的名字也就由此誕生了。

ZooKeeper 是用于維護(hù)配置信息,命名,提供分布式同步和提供組服務(wù)的集中式服務(wù)。所有這些類型的服務(wù)都以某種形式被分布式應(yīng)用程序使用。每次實(shí)施它們時(shí),都會(huì)進(jìn)行很多工作來(lái)修復(fù)不可避免的 bug 和競(jìng)爭(zhēng)條件。由于難以實(shí)現(xiàn)這類服務(wù),因此應(yīng)用程序最初通常會(huì)跳過(guò)它們,這會(huì)使它們?cè)诖嬖诟牡那闆r下變得脆弱并且難以管理。即使部署正確,這些服務(wù)的不同實(shí)現(xiàn)也會(huì)導(dǎo)致管理復(fù)雜。

ZooKeeper 的目標(biāo)是將這些不同服務(wù)的精華提煉為一個(gè)非常簡(jiǎn)單的接口,用于集中協(xié)調(diào)服務(wù)。服務(wù)本身是分布式的,并且高度可靠。服務(wù)將實(shí)現(xiàn)共識(shí),組管理和狀態(tài)協(xié)議,因此應(yīng)用程序不需要自己實(shí)現(xiàn)它們。

1.3 ZooKeeper工作機(jī)制

ZooKeeper 從設(shè)計(jì)模式角度來(lái)理解:就是一個(gè)基于觀察者模式設(shè)計(jì)的分布式服務(wù)管理框架,它負(fù)責(zé)存儲(chǔ)和管理大家都關(guān)心的數(shù)據(jù),然后接受觀察者的注冊(cè),一旦這些數(shù)據(jù)的狀態(tài)發(fā)生變化,ZK 就將負(fù)責(zé)通知已經(jīng)在 ZK 上注冊(cè)的那些觀察者做出相應(yīng)的反應(yīng),從而實(shí)現(xiàn)集群中類似 Master/Slave 管理模式。

1.4 特性

圖片來(lái)源:官網(wǎng)wiki
  1. ZooKeeper:一個(gè)領(lǐng)導(dǎo)者(leader),多個(gè)跟隨者(follower)組成的集群。

  2. Leader 負(fù)責(zé)進(jìn)行投票的發(fā)起和決議,更新系統(tǒng)狀態(tài)。

  3. Follower 用于接收客戶請(qǐng)求并向客戶端返回結(jié)果,在選舉 Leader 過(guò)程中參與投票。

  4. 集群中只要有半數(shù)以上節(jié)點(diǎn)存活,Zookeeper 集群就能正常服務(wù)。

  5. 全局?jǐn)?shù)據(jù)一致(單一視圖):每個(gè) Server 保存一份相同的數(shù)據(jù)副本,Client 無(wú)論連接到哪個(gè) Server,數(shù)據(jù)都是一致的。

  6. 順序一致性: 從同一客戶端發(fā)起的事務(wù)請(qǐng)求,最終將會(huì)嚴(yán)格地按照順序被應(yīng)用到 ZooKeeper 中去。

  7. 原子性: 所有事務(wù)請(qǐng)求的處理結(jié)果在整個(gè)集群中所有機(jī)器上的應(yīng)用情況是一致的,也就是說(shuō),要么整個(gè)集群中所有的機(jī)器都成功應(yīng)用了某一個(gè)事務(wù),要么都沒(méi)有應(yīng)用。

  8. 實(shí)時(shí)性,在一定時(shí)間范圍內(nèi),client 能讀到最新數(shù)據(jù)。

  9. 可靠性: 一旦一次更改請(qǐng)求被應(yīng)用,更改的結(jié)果就會(huì)被持久化,直到被下一次更改覆蓋。

1.5 設(shè)計(jì)目標(biāo)

  • 簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu) :Zookeeper 使得分布式程序能夠通過(guò)一個(gè)共享的樹(shù)形結(jié)構(gòu)的名字空間來(lái)進(jìn)行相互協(xié)調(diào),即Zookeeper 服務(wù)器內(nèi)存中的數(shù)據(jù)模型由一系列被稱為ZNode的數(shù)據(jù)節(jié)點(diǎn)組成,Zookeeper 將全量的數(shù)據(jù)存儲(chǔ)在內(nèi)存中,以此來(lái)提高服務(wù)器吞吐、減少延遲的目的。
  • 可以構(gòu)建集群 :Zookeeper 集群通常由一組機(jī)器構(gòu)成,組成 Zookeeper 集群的每臺(tái)機(jī)器都會(huì)在內(nèi)存中維護(hù)當(dāng)前服務(wù)器狀態(tài),并且每臺(tái)機(jī)器之間都相互通信。
  • 順序訪問(wèn) :對(duì)于來(lái)自客戶端的每個(gè)更新請(qǐng)求,Zookeeper 都會(huì)分配一個(gè)全局唯一的遞增編號(hào),這個(gè)編號(hào)反映了所有事務(wù)操作的先后順序。
  • 高性能 :Zookeeper 和 Redis 一樣全量數(shù)據(jù)存儲(chǔ)在內(nèi)存中,100% 讀請(qǐng)求壓測(cè) QPS 12-13W

1.6 數(shù)據(jù)結(jié)構(gòu)

Zookeeper 數(shù)據(jù)模型的結(jié)構(gòu)與 Unix 文件系統(tǒng)的結(jié)構(gòu)相似,整體上可以看做是一棵樹(shù),每個(gè)節(jié)點(diǎn)稱作一個(gè) 「ZNode」。每個(gè) ZNode 默認(rèn)能存儲(chǔ) 1MB 的數(shù)據(jù),每個(gè) ZNode 都可以通過(guò)其路徑唯一標(biāo)識(shí)。

1.7 應(yīng)用場(chǎng)景

ZooKeeper 是一個(gè)典型的分布式數(shù)據(jù)一致性解決方案,分布式應(yīng)用程序可以基于 ZooKeeper 實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布/訂閱、負(fù)載均衡、命名服務(wù)、分布式協(xié)調(diào)/通知、集群管理、Master 選舉、分布式鎖和分布式隊(duì)列等功能

統(tǒng)一命名服務(wù)

在分布式系統(tǒng)中,通過(guò)使用命名服務(wù),客戶端應(yīng)用能夠根據(jù)指定名字來(lái)獲取資源或服務(wù)的地址,提供者等信息。被命名的實(shí)體通??梢允羌褐械臋C(jī)器,提供的服務(wù)地址,進(jìn)程對(duì)象等等——這些我們都可以統(tǒng)稱他們?yōu)?strong>名字(Name)。其中較為常見(jiàn)的就是一些分布式服務(wù)框架(如RPC、RMI)中的服務(wù)地址列表。通過(guò)調(diào)用 Zookeeper 提供的創(chuàng)建節(jié)點(diǎn)的 API,能夠很容易創(chuàng)建一個(gè)全局唯一的 path,這個(gè) path 就可以作為一個(gè)名稱。

阿里巴巴開(kāi)源的分布式服務(wù)框架 Dubbo 就使用 ZooKeeper 來(lái)作為其命名服務(wù),維護(hù)全局的服務(wù)地址列表。

數(shù)據(jù)發(fā)布與訂閱(配置中心)

發(fā)布與訂閱模型,即所謂的配置中心,顧名思義就是發(fā)布者將數(shù)據(jù)發(fā)布到 ZooKeeper 節(jié)點(diǎn)上,供訂閱者動(dòng)態(tài)獲取數(shù)據(jù),實(shí)現(xiàn)配置信息的集中式管理和動(dòng)態(tài)更新。例如全局的配置信息,服務(wù)式服務(wù)框架的服務(wù)地址列表等就非常適合使用。

  1. 分布式環(huán)境下,配置文件管理和同步是一個(gè)常見(jiàn)問(wèn)題

    • 一個(gè)集群中,所有節(jié)點(diǎn)的配置信息是一致的,比如 Hadoop 集群、集群中的數(shù)據(jù)庫(kù)配置信息等全局配置

    • 對(duì)配置文件修改后,希望能夠快速同步到各個(gè)節(jié)點(diǎn)上。

  2. 配置管理可交由 ZooKeeper 實(shí)現(xiàn)

    • 可將配置信息寫(xiě)入 ZooKeeper 上的一個(gè) Znode
    • 各個(gè)節(jié)點(diǎn)監(jiān)聽(tīng)這個(gè) Znode
    • 一旦 Znode 中的數(shù)據(jù)被修改,ZooKeeper 將通知各個(gè)節(jié)點(diǎn)

統(tǒng)一集群管理

所謂集群管理無(wú)在乎兩點(diǎn):是否有機(jī)器退出和加入、選舉 Master。

管理節(jié)點(diǎn)
  1. 分布式環(huán)境中,實(shí)時(shí)掌握每個(gè)節(jié)點(diǎn)的狀態(tài)是必要的,比如我們要知道集群中各機(jī)器狀態(tài)、收集各個(gè)機(jī)器的運(yùn)行時(shí)狀態(tài)數(shù)據(jù)、服務(wù)器動(dòng)態(tài)上下線等。

  2. 交由 ZooKeeper 實(shí)現(xiàn)的方式

    • 可將節(jié)點(diǎn)信息寫(xiě)入 ZooKeeper 上的一個(gè) Znode
    • 監(jiān)聽(tīng)這個(gè) Znode 可獲取它的實(shí)時(shí)狀態(tài)變化
    • 典型應(yīng)用:HBase 中 Master 狀態(tài)監(jiān)控和選舉。(TODO:圖應(yīng)該是注冊(cè)和Register and watch)
Master選舉

在分布式環(huán)境中,相同的業(yè)務(wù)應(yīng)用分布在不同的機(jī)器上,有些業(yè)務(wù)邏輯(例如一些耗時(shí)的計(jì)算,網(wǎng)絡(luò)I/O處理),往往只需要讓整個(gè)集群中的某一臺(tái)機(jī)器進(jìn)行執(zhí)行,其余機(jī)器可以共享這個(gè)結(jié)果,這樣可以大大減少重復(fù)勞動(dòng),提高性能,于是這個(gè)master選舉便是這種場(chǎng)景下的碰到的主要問(wèn)題。

利用 Zookeeper 的強(qiáng)一致性,能夠很好的保證在分布式高并發(fā)情況下節(jié)點(diǎn)的創(chuàng)建一定是全局唯一的,即:同時(shí)有多個(gè)客戶端請(qǐng)求創(chuàng)建 /currentMaster 節(jié)點(diǎn),最終一定只有一個(gè)客戶端請(qǐng)求能夠創(chuàng)建成功。Zookeeper 通過(guò)這種節(jié)點(diǎn)唯一的特性,可以創(chuàng)建一個(gè) Master 節(jié)點(diǎn),其他客戶端 Watcher 監(jiān)控當(dāng)前 Master 是否存活,一旦 Master 掛了,其他機(jī)器再創(chuàng)建這樣的一個(gè) Master 節(jié)點(diǎn),用來(lái)重新選舉。

軟負(fù)載均衡

分布式系統(tǒng)中,負(fù)載均衡是一種很普遍的技術(shù),為了保證高可用性,通常同一個(gè)應(yīng)用或同一個(gè)服務(wù)的提供方都會(huì)部署多份,達(dá)到對(duì)等服務(wù)??梢允怯布呢?fù)載均衡,如 F5,也可以是軟件的負(fù)載,我們熟知的 Nginx,或者這里介紹的 Zookeeper。

分布式協(xié)調(diào)/通知

Zookeeper 中特有的 「Watcher」 注冊(cè)與異步通知機(jī)制,能夠很好的實(shí)現(xiàn)分布式環(huán)境下不同機(jī)器,甚至不同系統(tǒng)之間的協(xié)調(diào)和通知,從而實(shí)現(xiàn)對(duì)數(shù)據(jù)變更的實(shí)時(shí)處理。

使用方法通常是不同系統(tǒng)都對(duì) ZK 上同一個(gè) znode 進(jìn)行注冊(cè),監(jiān)聽(tīng) znode 的變化(包括 znode 本身內(nèi)容及子節(jié)點(diǎn)的),其中一個(gè)系統(tǒng) update 了 znode,那么另一個(gè)系統(tǒng)能夠收到通知,并作出相應(yīng)處理。

  • 心跳檢測(cè)中可以讓檢測(cè)系統(tǒng)和被檢測(cè)系統(tǒng)之間并不直接關(guān)聯(lián)起來(lái),而是通過(guò) ZK 上某個(gè)節(jié)點(diǎn)關(guān)聯(lián),減少系統(tǒng)耦合;
  • 系統(tǒng)調(diào)度模式中,假設(shè)某系統(tǒng)有控制臺(tái)和推送系統(tǒng)兩部分組成,控制臺(tái)的職責(zé)是控制推送系統(tǒng)進(jìn)行相應(yīng)的推送工作。管理人員在控制臺(tái)作的一些操作,實(shí)際上是修改了 ZK 上某些節(jié)點(diǎn)的狀態(tài),而 ZK 就把這些變化通知給他們注冊(cè) Watcher 的客戶端,即推送系統(tǒng),于是,作出相應(yīng)的推送任務(wù)。

分布式鎖

分布式鎖,這個(gè)主要得益于 ZooKeeper 為我們保證了數(shù)據(jù)的強(qiáng)一致性。

鎖服務(wù)可以分為兩類,一個(gè)是保持獨(dú)占,另一個(gè)是控制時(shí)序。

  • 所謂保持獨(dú)占,就是所有試圖來(lái)獲取這個(gè)鎖的客戶端,最終只有一個(gè)可以成功獲得這把鎖。通常的做法是把 zk 上的一個(gè) znode 看作是一把鎖,通過(guò) create znode 的方式來(lái)實(shí)現(xiàn)。所有客戶端都去創(chuàng)建 /distribute_lock 節(jié)點(diǎn),最終成功創(chuàng)建的那個(gè)客戶端也即擁有了這把鎖。

  • 控制時(shí)序,就是所有試圖來(lái)獲取這個(gè)鎖的客戶端,最終都是會(huì)被安排執(zhí)行,只是有個(gè)全局時(shí)序了。做法和上面基本類似,只是這里 /distribute_lock 已預(yù)先存在,客戶端在它下面創(chuàng)建臨時(shí)有序節(jié)點(diǎn)(這個(gè)可以通過(guò)節(jié)點(diǎn)的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來(lái)指定)。ZK 的父節(jié)點(diǎn)(/distribute_lock)維持一份 sequence,保證子節(jié)點(diǎn)創(chuàng)建的時(shí)序性,從而也形成了每個(gè)客戶端的全局時(shí)序。

個(gè)人感覺(jué)還是用 Redis 實(shí)現(xiàn)分布式鎖更加方便。

PS:阿里中間件團(tuán)隊(duì):“其實(shí),ZK 并非天生就是為這些應(yīng)用場(chǎng)景設(shè)計(jì)的,都是后來(lái)眾多開(kāi)發(fā)者根據(jù)其框架的特性,利用其提供的一系列API接口(或者稱為原語(yǔ)集),摸索出來(lái)的典型使用方法?!?/p>


2. Hello ZooKeeper

ZooKeeper 的三種部署方式:

  1. 單機(jī)模式,即部署在單臺(tái)機(jī)器上的一個(gè) ZK 服務(wù),適用于學(xué)習(xí)、了解 ZK 基礎(chǔ)功能
  2. 偽分布模式,即部署在一臺(tái)機(jī)器上的多個(gè)(原則上大于3個(gè))ZK 服務(wù),偽集群,適用于學(xué)習(xí)、開(kāi)發(fā)和測(cè)試
  3. 全分布式模式(復(fù)制模式),即在多臺(tái)機(jī)器上部署服務(wù),真正的集群模式,生產(chǎn)環(huán)境中使用

計(jì)劃寫(xiě)三篇的,第二篇會(huì)實(shí)戰(zhàn) coding,運(yùn)用各種 API,到時(shí)候再裝集群,本節(jié)先來(lái)個(gè)單機(jī)玩~~

2.1 本地模式安裝部署

2.1.1 安裝前準(zhǔn)備

  1. 安裝 Jdk

  2. 拷貝或下載 Zookeeper 安裝包到 Linux 系統(tǒng)下(這里有個(gè)小問(wèn)題,如果你下載 ZK 版本是3.5+ 的話,要下載 bin.tar.gz,愚笨的我最先沒(méi)看到官網(wǎng)說(shuō)明,一頓操作各種報(bào)錯(cuò)找不到 Main 方法)

  3. 解壓到指定目錄

    tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz

2.1.2 配置修改

  1. 將 zookeeper-3.5.7/conf 這個(gè)路徑下的 zoo_sample.cfg 修改為 zoo.cfg ;

    mv zoo_sample.cfg zoo.cfg
  2. 打開(kāi) zoo.cfg 文件,修改 dataDir 路徑:

    dataDir=XXX/zookeeper-3.5.7/zkData

2.1.3 操作 Zookeeper

  1. 啟動(dòng) Zookeeper:  bin/zkServer.sh start
/usr/local/bin/java
ZooKeeper JMX enabled by default
Using config: /home/sync360/test/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
  1. 查看進(jìn)程是否啟動(dòng): jps
4020 Jps
4001 QuorumPeerMain
  1. 查看狀態(tài):bin/zkServer.sh status
/usr/local/bin/java
ZooKeeper JMX enabled by default
Using config: /home/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone
  1. 啟動(dòng)客戶端:bin/zkCli.sh
Connecting to localhost:2181
2020-03-25 15:41:19,112 [myid:] - INFO  [main:Environment@109] - Client environment:zookeeper.version=3.5.7-f0fdd52973d373ffd9c86b81d99842dc2c7f660e, built on 02/10/2020 11:30 GMT

...

2020-03-25 15:41:19,183 [myid:] - INFO  [main:ClientCnxn@1653] - zookeeper.request.timeout value is 0. feature enabled=
Welcome to ZooKeeper!

...

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
  1. 退出客戶端:quit

  2. 停止 Zookeeper:  bin/zkServer.sh stop

2.2 常用命令

命令基本語(yǔ)法功能描述
help顯示所有操作命令
ls path [watch]使用 ls 命令來(lái)查看當(dāng)前znode中所包含的內(nèi)容
ls2 path [watch]查看當(dāng)前節(jié)點(diǎn)數(shù)據(jù)并能看到更新次數(shù)等數(shù)據(jù)
create普通創(chuàng)建-s  含有序列-e  臨時(shí)(重啟或者超時(shí)消失)
get path [watch]獲得節(jié)點(diǎn)的值
set設(shè)置節(jié)點(diǎn)的具體值
stat查看節(jié)點(diǎn)狀態(tài)
delete刪除節(jié)點(diǎn)
rmr遞歸刪除節(jié)點(diǎn)

ls 查看當(dāng)前 zk 中所包含的內(nèi)容

[zk: localhost:2181(CONNECTED) 1] ls /
[lazyegg, zookeeper]

create 創(chuàng)建一個(gè)新的 znode

[zk: localhost:2181(CONNECTED) 2] create /test
Created /test

get 查看新的 znode 的值

[zk: localhost:2181(CONNECTED) 4] get /test
null

可以看到值為 null,我們剛才設(shè)置了一個(gè)沒(méi)有值的節(jié)點(diǎn),也可以通過(guò) create /zoo dog 直接創(chuàng)建有內(nèi)容的節(jié)點(diǎn)

set 對(duì) zk 所關(guān)聯(lián)的字符串進(jìn)行設(shè)置

set /test hello

delete 刪除節(jié)點(diǎn)

delete /test

2.3 配置參數(shù)解讀

在 Zookeeper 的設(shè)計(jì)中,如果是集群模式,那所有機(jī)器上的 zoo.cfg 文件內(nèi)容應(yīng)該都是一致的。

Zookeeper 中的配置文件 zoo.cfg 中參數(shù)含義解讀如下:

  • tickTime =2000:通信心跳數(shù)

    Zookeeper 使用的基本時(shí)間,服務(wù)器之間或客戶端與服務(wù)器之間維持心跳的時(shí)間間隔,也就是每個(gè) tickTime時(shí)間就會(huì)發(fā)送一個(gè)心跳,時(shí)間單位為毫秒

    它用于心跳機(jī)制,并且設(shè)置最小的 session 超時(shí)時(shí)間為兩倍心跳時(shí)間。(session的最小超時(shí)時(shí)間是2*tickTime);

  • initLimit =10:主從初始通信時(shí)限,集群中的 Follower 跟隨者服務(wù)器與 Leader 領(lǐng)導(dǎo)者服務(wù)器之間初始連接時(shí)能容忍的最多心跳數(shù)(tickTime的數(shù)量),用它來(lái)限定集群中的 ZK 服務(wù)器連接到 Leader 的時(shí)限;

  • syncLimit =5:主從同步通信時(shí)限,集群中 Leader 與 Follower 之間的最大響應(yīng)時(shí)間單位,假如響應(yīng)超過(guò)syncLimit * tickTime,Leader 認(rèn)為 Follwer 死掉,從服務(wù)器列表中刪除 Follwer;

  • dataDir:數(shù)據(jù)文件目錄+數(shù)據(jù)持久化路徑;

  • clientPort =2181:客戶端連接端口

3. 你要知道的概念

  • ZooKeeper 本身就是一個(gè)分布式程序(只要半數(shù)以上節(jié)點(diǎn)存活,ZooKeeper 就能正常服務(wù))。
  • 為了保證高可用,最好是以集群形態(tài)來(lái)部署 ZooKeeper,這樣只要集群中大部分機(jī)器是可用的(能夠容忍一定的機(jī)器故障),那么 ZooKeeper 本身仍然是可用的。
  • ZooKeeper 將數(shù)據(jù)保存在內(nèi)存中,這也就保證了高吞吐量和低延遲(但是內(nèi)存限制了能夠存儲(chǔ)的容量不太大,此限制也是保持 znode 中存儲(chǔ)的數(shù)據(jù)量較小的進(jìn)一步原因)。
  • ZooKeeper 是高性能的。在“讀”多于“寫(xiě)”的應(yīng)用程序中尤其的高性能,因?yàn)椤皩?xiě)”會(huì)導(dǎo)致所有的服務(wù)器間同步狀態(tài)。(“讀”多于“寫(xiě)”是協(xié)調(diào)服務(wù)的典型場(chǎng)景。)
  • ZooKeeper 底層其實(shí)只提供了兩個(gè)功能:
    • 管理(存儲(chǔ)、讀取)用戶程序提交的數(shù)據(jù)
    • 為用戶程序提交數(shù)據(jù)節(jié)點(diǎn)監(jiān)聽(tīng)服務(wù)

這里引入一個(gè)簡(jiǎn)單的例子,逐個(gè)介紹一些 ZK 中的概念。

在分布式系統(tǒng)中經(jīng)常會(huì)遇到這種情況,多個(gè)應(yīng)用讀取同一個(gè)配置。例如:Client1,Client2 兩個(gè)應(yīng)用都會(huì)讀取配置 B 中的內(nèi)容,一旦 B 中的內(nèi)容出現(xiàn)變化,就會(huì)通知 Client1 和 Client2。

一般的做法是在 Client1,Client2 中按照時(shí)鐘頻率詢問(wèn) B 的變化,或者使用觀察者模式來(lái)監(jiān)聽(tīng) B 的變化,發(fā)現(xiàn)變化以后再更新兩個(gè)客戶端。那么 ZooKeeper 如何協(xié)調(diào)這種場(chǎng)景?

這兩個(gè)客戶端連接到 ZooKeeper 的服務(wù)器,并獲取其中存放的 B。保存 B 值的地方在 ZooKeeper 服務(wù)端中就稱為 ZNode

3.1 數(shù)據(jù)節(jié)點(diǎn)(Znode)

在談到分布式的時(shí)候,我們通常說(shuō)的“節(jié)點(diǎn)'是指組成集群的每一臺(tái)機(jī)器。然而,在 Zookeeper 中,“節(jié)點(diǎn)'分為兩類,第一類同樣是指構(gòu)成集群的機(jī)器,我們稱之為「機(jī)器節(jié)點(diǎn)」;第二類則是指數(shù)據(jù)模型中的數(shù)據(jù)單元,我們稱之為「數(shù)據(jù)節(jié)點(diǎn)」一一ZNode。上圖中的 A、B 就是一個(gè)數(shù)據(jù)結(jié)點(diǎn)。

Zookeeper 將所有數(shù)據(jù)存儲(chǔ)在內(nèi)存中,數(shù)據(jù)模型是一棵樹(shù)(Znode Tree),由斜杠(/)進(jìn)行分割的路徑,就是一個(gè) Znode,例如 /Configuration/B。每個(gè) Znode 上都會(huì)保存自己的數(shù)據(jù)內(nèi)容,同時(shí)還會(huì)保存一系列屬性信息。

在 Zookeeper 中,Znode 可以分為持久節(jié)點(diǎn)臨時(shí)節(jié)點(diǎn)兩類。

  • 所謂持久節(jié)點(diǎn)是指一旦這個(gè) ZNode 被創(chuàng)建了,除非主動(dòng)進(jìn)行 ZNode 的移除操作,否則這個(gè) ZNode 將一直保存在 Zookeeper 上。
  • 而臨時(shí)節(jié)點(diǎn)就不一樣了,它的生命周期和客戶端會(huì)話綁定,一旦客戶端會(huì)話失效,那么這個(gè)客戶端創(chuàng)建的所有臨時(shí)節(jié)點(diǎn)都會(huì)被移除。

另外,ZooKeeper 還允許用戶為每個(gè)節(jié)點(diǎn)添加一個(gè)特殊的屬性:**SEQUENTIAL。**也被叫做 順序結(jié)點(diǎn),一旦節(jié)點(diǎn)被標(biāo)記上這個(gè)屬性,那么在這個(gè)節(jié)點(diǎn)被創(chuàng)建的時(shí)候,Zookeeper 會(huì)自動(dòng)在其節(jié)點(diǎn)名后面追加上一個(gè)整型數(shù)字,這個(gè)整型數(shù)字是一個(gè)由父節(jié)點(diǎn)維護(hù)的自增數(shù)字。

3.2 事件監(jiān)聽(tīng)器(Watcher)

上面說(shuō)了 ZooKeeper 用來(lái)存放數(shù)據(jù)的 ZNode,并且把 B 的值存儲(chǔ)在里面。如果 B 被更新了,兩個(gè)客戶端(Client1、Client2)如何獲得通知呢?

Zookeeper 允許用戶在指定節(jié)點(diǎn)上注冊(cè)一些 Watcher,當(dāng) Znode 發(fā)生變化時(shí),將觸發(fā)并刪除一個(gè) watch。當(dāng) watch 被觸發(fā)時(shí)客戶端會(huì)收到一個(gè)數(shù)據(jù)包,指示 znode 已經(jīng)被修改。如果客戶端和 ZooKeeper 服務(wù)器之間的連接中斷,客戶端將收到本地通知。該機(jī)制是 Zookeeper 實(shí)現(xiàn)分布式協(xié)調(diào)服務(wù)的重要特性

3.6.0中的新增功能:客戶端還可以在 znode 上設(shè)置永久性的遞歸監(jiān)視,這些監(jiān)視在觸發(fā)時(shí)不會(huì)刪除,并且會(huì)以遞歸方式觸發(fā)已注冊(cè) znode 以及所有子 znode 的更改。

ZooKeeper 客戶端(Client)會(huì)在指定的節(jié)點(diǎn)(/Configuration/B)上注冊(cè)一個(gè) Watcher,ZNode 上的 B 被更新的時(shí)候,服務(wù)端就會(huì)通知 Client1 和 Client2。

3.3 版本

有了 Watcher 機(jī)制,就可以實(shí)現(xiàn)分布式協(xié)調(diào)/通知了,假設(shè)有這樣的場(chǎng)景,兩個(gè)客戶端同時(shí)對(duì) B 進(jìn)行寫(xiě)入操作,這兩個(gè)客戶端就會(huì)存在競(jìng)爭(zhēng)關(guān)系,通常需要對(duì) B 進(jìn)行加鎖操作,ZK 通過(guò) version 版本號(hào)來(lái)控制實(shí)現(xiàn)樂(lè)觀鎖中的“寫(xiě)入校驗(yàn)”機(jī)制。

Zookeeper 的每個(gè) ZNode 上都會(huì)存儲(chǔ)數(shù)據(jù),對(duì)應(yīng)于每個(gè) ZNode,Zookeeper 都會(huì)為其維護(hù)一個(gè)叫作 Stat 的數(shù)據(jù)結(jié)構(gòu),Stat 中記錄了這個(gè) ZNode 的三個(gè)數(shù)據(jù)版本,分別是 version(當(dāng)前ZNode的版本)、cversion(當(dāng)前ZNode 子節(jié)點(diǎn)的版本)和 aversion(當(dāng)前ZNode的ACL版本)。

znode 里都有些啥呢?

3.4 Stat 結(jié)構(gòu)體

Znodes 維護(hù)了一個(gè) stat 結(jié)構(gòu),其中包含數(shù)據(jù)更改、ACL更改的版本號(hào)、時(shí)間戳等。

狀態(tài)屬性說(shuō)明
czxid創(chuàng)建節(jié)點(diǎn)的事務(wù)zxid。每次修改 ZK 狀態(tài)都會(huì)收到一個(gè)zxid形式的時(shí)間戳,也就是 ZK 事務(wù)ID。事務(wù)ID是 ZK 中所有修改總的次序。每個(gè)修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前發(fā)生
ctimeznode被創(chuàng)建的毫秒數(shù)(從1970年開(kāi)始)
mzxidznode最后更新的事務(wù)zxid
mtimeznode最后修改的毫秒數(shù)(從1970年開(kāi)始)
pzxidznode最后更新的子節(jié)點(diǎn)zxid
version數(shù)據(jù)節(jié)點(diǎn)版本號(hào)
cversion子節(jié)點(diǎn)版本號(hào),znode子節(jié)點(diǎn)修改次數(shù)
aversionznode訪問(wèn)控制列表的變化號(hào)
ephemeralOwner如果是臨時(shí)節(jié)點(diǎn),這個(gè)是znode擁有者的session id。如果不是臨時(shí)節(jié)點(diǎn)則是0
dataLengthznode的數(shù)據(jù)長(zhǎng)度
numChildrenznode子節(jié)點(diǎn)數(shù)量

3.5 會(huì)話(Session)

Session 指的是 ZooKeeper 服務(wù)器與客戶端會(huì)話。

在 ZooKeeper 中,一個(gè)客戶端連接是指客戶端和服務(wù)器之間的一個(gè) TCP 長(zhǎng)連接??蛻舳藛?dòng)的時(shí)候,首先會(huì)與服務(wù)器建立一個(gè) TCP 連接,從第一次連接建立開(kāi)始,客戶端會(huì)話的生命周期也開(kāi)始了。通過(guò)這個(gè)連接,客戶端能夠通過(guò)心跳檢測(cè)與服務(wù)器保持有效的會(huì)話,也能夠向 Zookeeper 服務(wù)器發(fā)送請(qǐng)求并接受響應(yīng),同時(shí)還能夠通過(guò)該連接接收來(lái)自服務(wù)器的 Watch 事件通知。

Session 作為會(huì)話實(shí)體,用來(lái)代表客戶端會(huì)話,其包括 4 個(gè)屬性:

  • SessionID,用來(lái)全局唯一識(shí)別會(huì)話;
  • TimeOut,會(huì)話超時(shí)事件??蛻舳嗽趧?chuàng)造 Session 實(shí)例的時(shí)候,會(huì)設(shè)置一個(gè)會(huì)話超時(shí)的時(shí)間。當(dāng)由于服務(wù)器壓力太大、網(wǎng)絡(luò)故障或是客戶端主動(dòng)斷開(kāi)連接等各種原因?qū)е驴蛻舳诉B接斷開(kāi)時(shí),只要在 sessionTimeout 規(guī)定的時(shí)間內(nèi)能夠重新連接上集群中任意一臺(tái)服務(wù)器,那么之前創(chuàng)建的會(huì)話仍然有效;
  • TickTime,下次會(huì)話超時(shí)時(shí)間點(diǎn);
  • isClosing,當(dāng)服務(wù)端如果檢測(cè)到會(huì)話超時(shí)失效了,會(huì)通過(guò)設(shè)置這個(gè)屬性將會(huì)話關(guān)閉。

3.6 ACL

Zookeeper 采用 ACL(Access Control Lists)策略來(lái)進(jìn)行權(quán)限控制,類似于 UNIX 文件系統(tǒng)的權(quán)限控制。Zookeeper 定義了如下 5 種權(quán)限:

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

其中尤其需要注意的是,CREATE 和 DELETE 這兩種權(quán)限都是針對(duì)子節(jié)點(diǎn)的權(quán)限控制。

3.7 集群角色

最典型集群模式:Master/Slave 模式(主備模式)。在這種模式中,通常 Master 服務(wù)器作為主服務(wù)器提供寫(xiě)服務(wù),其他的 Slave 從服務(wù)器通過(guò)異步復(fù)制的方式獲取 Master 服務(wù)器最新的數(shù)據(jù)提供讀服務(wù)。

但是,在 ZooKeeper 中沒(méi)有選擇傳統(tǒng)的 Master/Slave 概念,而是引入了Leader、FollowerObserver 三種角色。

  • Leader:為客戶端提供讀和寫(xiě)的服務(wù),負(fù)責(zé)投票的發(fā)起和決議,更新系統(tǒng)狀態(tài)
  • Follower:為客戶端提供讀服務(wù),如果是寫(xiě)服務(wù)則轉(zhuǎn)發(fā)給 Leader。在選舉過(guò)程中參與投票
  • Observer:為客戶端提供讀服務(wù)器,如果是寫(xiě)服務(wù)則轉(zhuǎn)發(fā)給 Leader。不參與選舉過(guò)程中的投票,也不參與“過(guò)半寫(xiě)成功”策略。在不影響寫(xiě)性能的情況下提升集群的讀性能。此角色是在 zookeeper3.3 系列新增的角色。
server 狀態(tài)
  • LOOKING:尋找Leader狀態(tài)
  • LEADING:領(lǐng)導(dǎo)者狀態(tài),表明當(dāng)前服務(wù)器角色是 Leader
  • FOLLOWING:跟隨者狀態(tài),表明當(dāng)前服務(wù)器角色是 Follower
  • OBSERVING:觀察者狀態(tài),表明當(dāng)前服務(wù)器角色是 Observer

選舉機(jī)制

zk-vote
  1. 服務(wù)器1啟動(dòng),此時(shí)只有它一臺(tái)服務(wù)器啟動(dòng)了,它發(fā)出去的報(bào)文沒(méi)有任何響應(yīng),所以它的選舉狀態(tài)一直是LOOKING 狀態(tài)。
  2. 服務(wù)器2啟動(dòng),它與最開(kāi)始啟動(dòng)的服務(wù)器1進(jìn)行通信,互相交換自己的選舉結(jié)果,由于兩者都沒(méi)有歷史數(shù)據(jù),所以 id 值較大的服務(wù)器2勝出,但是由于沒(méi)有達(dá)到超過(guò)半數(shù)以上的服務(wù)器都同意選舉它(這個(gè)例子中的半數(shù)以上是3),所以服務(wù)器1、2還是繼續(xù)保持 LOOKING 狀態(tài)。
  3. 服務(wù)器3啟動(dòng),根據(jù)前面的理論分析,服務(wù)器3成為服務(wù)器1、2、3中的老大,而與上面不同的是,此時(shí)有三臺(tái)服務(wù)器選舉了它,所以它成為了這次選舉的Leader。
  4. 服務(wù)器4啟動(dòng),根據(jù)前面的分析,理論上服務(wù)器4應(yīng)該是服務(wù)器1、2、3、4中最大的,但是由于前面已經(jīng)有半數(shù)以上的服務(wù)器選舉了服務(wù)器3,所以它只能接受當(dāng)小弟的命了。
  5. 服務(wù)器5啟動(dòng),同4一樣當(dāng)小弟。

Watcher 監(jiān)聽(tīng)器

Zookeeper 中最有特色且最不容易理解的是監(jiān)視(Watches)。

Zookeeper 所有的讀操作——getData()getChildren(), 和 exists() 都可以設(shè)置監(jiān)視(watch),監(jiān)視事件可以理解為一次性的觸發(fā)器, 官方定義如下:a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。對(duì)此需要作出如下理解:

  • One-time trigger(一次性觸發(fā))

    當(dāng)設(shè)置監(jiān)視的數(shù)據(jù)發(fā)生改變時(shí),該監(jiān)視事件會(huì)被發(fā)送到客戶端,例如,如果客戶端調(diào)用了 getData('/znode1', true) 并且稍后 /znode1 節(jié)點(diǎn)上的數(shù)據(jù)發(fā)生了改變或者被刪除了,客戶端將會(huì)獲取到 /znode1 發(fā)生變化的監(jiān)視事件,而如果 /znode1 再一次發(fā)生了變化,除非客戶端再次對(duì) /znode1 設(shè)置監(jiān)視,否則客戶端不會(huì)收到事件通知。(3.6之后可以設(shè)置永久監(jiān)視)

  • Sent to the client(發(fā)送至客戶端)

    Zookeeper 客戶端和服務(wù)端是通過(guò) socket 進(jìn)行通信的,由于網(wǎng)絡(luò)存在故障,所以監(jiān)視事件很有可能不會(huì)成功到達(dá)客戶端,監(jiān)視事件是異步發(fā)送至監(jiān)視者的,Zookeeper 本身提供了保序性(ordering guarantee):即客戶端只有首先看到了監(jiān)視事件后,才會(huì)感知到它所設(shè)置監(jiān)視的 znode 發(fā)生了變化(a client will never see a change for which it has set a watch until it first sees the watch event)。網(wǎng)絡(luò)延遲或者其他因素可能導(dǎo)致不同的客戶端在不同的時(shí)刻感知某一監(jiān)視事件,但是不同的客戶端所看到的一切具有一致的順序。

  • The data for which the watch was set(被設(shè)置 watch 的數(shù)據(jù))

    這意味著 znode 節(jié)點(diǎn)本身具有不同的改變方式。你也可以想象 Zookeeper 維護(hù)了兩條監(jiān)視鏈表:數(shù)據(jù)監(jiān)視和子節(jié)點(diǎn)監(jiān)視(data watches and child watches), getData()exists() 設(shè)置數(shù)據(jù)監(jiān)視,getChildren() 設(shè)置子節(jié)點(diǎn)監(jiān)視?;蛘?,你也可以想象 Zookeeper 設(shè)置的不同監(jiān)視返回不同的數(shù)據(jù),getData()exists()返回 znode 節(jié)點(diǎn)的相關(guān)信息,而 getChildren() 返回子節(jié)點(diǎn)列表。因此, setData() 會(huì)觸發(fā)設(shè)置在某一節(jié)點(diǎn)上所設(shè)置的數(shù)據(jù)監(jiān)視(假定數(shù)據(jù)設(shè)置成功),而一次成功的 create() 操作則會(huì)觸發(fā)當(dāng)前節(jié)點(diǎn)上所設(shè)置的數(shù)據(jù)監(jiān)視以及父節(jié)點(diǎn)的子節(jié)點(diǎn)監(jiān)視。一次成功的 delete() 操作將會(huì)觸發(fā)當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)監(jiān)視和子節(jié)點(diǎn)監(jiān)視事件,同時(shí)也會(huì)觸發(fā)該節(jié)點(diǎn)父節(jié)點(diǎn)的 child watch。

Zookeeper 中的監(jiān)視是輕量級(jí)的,因此容易設(shè)置、維護(hù)和分發(fā)。當(dāng)客戶端與 Zookeeper 服務(wù)器端失去聯(lián)系時(shí),客戶端并不會(huì)收到監(jiān)視事件的通知,只有當(dāng)客戶端重新連接后,若在必要的情況下,以前注冊(cè)的監(jiān)視會(huì)重新被注冊(cè)并觸發(fā),對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)這通常是透明的。只有一種情況會(huì)導(dǎo)致監(jiān)視事件的丟失,即:通過(guò) exists() 設(shè)置了某個(gè) znode 節(jié)點(diǎn)的監(jiān)視,但是如果某個(gè)客戶端在此 znode 節(jié)點(diǎn)被創(chuàng)建和刪除的時(shí)間間隔內(nèi)與 zookeeper 服務(wù)器失去了聯(lián)系,該客戶端即使稍后重新連接 zookeepe r服務(wù)器后也得不到事件通知。

圖片來(lái)源:yht7

從上圖可以看到,Watcher 機(jī)制包括三個(gè)角色:客戶端線程、客戶端的 WatchManager 以及 ZooKeeper 服務(wù)器。Watcher 機(jī)制就是這三個(gè)角色之間的交互,整個(gè)過(guò)程分為注冊(cè)、存儲(chǔ)和通知三個(gè)步驟:

  1. 客戶端向 ZooKeeper 服務(wù)器注冊(cè)一個(gè) Watcher 監(jiān)聽(tīng);
  2. 把這個(gè)監(jiān)聽(tīng)信息存儲(chǔ)到客戶端的 WatchManager 中;
  3. 當(dāng) ZooKeeper 中的節(jié)點(diǎn)發(fā)生變化時(shí),會(huì)通知客戶端,客戶端會(huì)調(diào)用相應(yīng) Watcher 對(duì)象中的回調(diào)方法。

也不知道有木有人對(duì)下一篇的實(shí)戰(zhàn)環(huán)節(jié)感興趣~~~~~

參考:

《從Paxos到ZooKeeper 分布式一致性原理與實(shí)踐》

《阿里中間件團(tuán)隊(duì)博客》http://jm./2011/10/08/1232/

《Zookeeper官方文檔》https://zookeeper./doc/

《尚硅谷Zookeeper》

https://cloud.tencent.com/developer/article/1578401

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    日韩高清一区二区三区四区| 欧美成人免费视频午夜色| 国产亚洲欧美日韩国亚语| 日本一品道在线免费观看| 精品人妻少妇二区三区| 国产精品午夜性色视频| 美女被后入福利在线观看| 精品高清美女精品国产区| 好东西一起分享老鸭窝| 欧美亚洲91在线视频| 亚洲一区二区精品国产av| 精品午夜福利无人区乱码| 日韩精品视频免费观看| 国内女人精品一区二区三区| 欧美整片精品日韩综合| 日韩欧美一区二区不卡视频| 国产午夜精品美女露脸视频| 丰满熟女少妇一区二区三区| 香蕉久久夜色精品国产尤物| 国产精品一级香蕉一区| 免费午夜福利不卡片在线 视频| 午夜精品一区免费视频| 欧美在线视频一区观看| 欧美日韩国产精品第五页| 欧美自拍偷自拍亚洲精品| 丝袜破了有美女肉体免费观看 | 亚洲一区二区三在线播放| 国产亚洲成av人在线观看| 久久免费精品拍拍一区二区| 欧美多人疯狂性战派对| 久热青青草视频在线观看| 欧美黑人在线一区二区| 国产精品亚洲欧美一区麻豆 | 中文字幕一区二区熟女| 国产熟女一区二区三区四区| 亚洲男人的天堂就去爱| 国产av一区二区三区四区五区| 亚洲中文字幕视频一区二区| 国产毛片不卡视频在线| 高清在线精品一区二区| 亚洲国产丝袜一区二区三区四 |

    AI助手

    阅读时有疑惑?点击向AI助手提问吧

    联系客服

    微信扫码,添加客服企业微信

    客服QQ:

    1732698931

    联系电话:4000-999-276

    客服工作时间9:00-18:00,晚上非工作时间,请在微信或QQ留言,第二天客服上班后会立即联系您。