實現配置信息的集中式管理和數據的動態更新java
實現配置中心有兩種模式:push 、pull。mysql
長輪訓git
zookeeper採用的是推拉相結合的方式。 客戶端向服務器端註冊本身須要關注的節點。一旦節點數據發生變化,那麼服務器端就會向客戶端github
發送watcher事件通知。客戶端收到通知後,主動到服務器端獲取更新後的數據redis
請求/數據分攤多個計算機單元上算法
一般實現分佈式鎖有幾種方式sql
數據方式去實現apache
建立一個表, 經過索引惟一的方式 create table (id , methodname …) methodname增長惟一索引 insert 一條數據XXX delete 語句刪除這條記錄 mysql for update 行鎖,杜佔鎖
排他鎖api
利用路徑惟一安全
共享鎖(讀鎖)
locks當中是有序節點,控制使用權限,每個客戶端寫一個節點以後,獲取到最小節點,獲取數據,有寫的操做,優先處理寫的節點,利用節點特性
package zk.lock; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import java.io.IOException; import java.util.List; import java.util.Random; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class DistributeLock { //根節點 private static final String ROOT_LOCKS = "/LOCKS"; private ZooKeeper zooKeeper; //節點的數據 private static final byte[] data = {1, 2}; //會話超時時間 private int sessionTimeOut; //記錄鎖節點id private String lockID; private CountDownLatch countDownLatch = new CountDownLatch(1); public DistributeLock() throws IOException, InterruptedException { this.zooKeeper = ZookeeperClient.getInstance(); this.sessionTimeOut = ZookeeperClient.getSESSIONTIMEOUT(); } /** * 獲取鎖的方法 * * @return */ public synchronized boolean lock() { try { //LOCKS/000001 lockID = zooKeeper.create(ROOT_LOCKS + "/", data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println(Thread.currentThread().getName() + "->" + "成功建立了lock節點[" + lockID + ",開始競爭鎖]"); //獲取根節點下的全部子節點 List<String> childrenNodes = zooKeeper.getChildren(ROOT_LOCKS, true); // 排序,從小到大 TreeSet<String> sortedSet = new TreeSet<>(); for (String children : childrenNodes) { sortedSet.add(ROOT_LOCKS + "/" + children); } //獲取到最小的節點 String first = sortedSet.first(); if (lockID.equals(first)) { //表示當前就是最小的節點 System.out.println(Thread.currentThread().getName() + "-->成功得到鎖,locak節點爲:【" + lockID + "]"); return true; } SortedSet<String> lessThanLockId = sortedSet.headSet(lockID); if (!lessThanLockId.isEmpty()) { //獲取到比當前LockId這個節點更小的上一個節點 String prevLockId = lessThanLockId.last(); //監控上一個節點 zooKeeper.exists(prevLockId, new LockWatcher(countDownLatch)); //若是會話超時或者節點被刪除(釋放)了 countDownLatch.await(sessionTimeOut, TimeUnit.MILLISECONDS); System.out.println(Thread.currentThread().getName() + "成功獲取鎖:【" + lockID + "】"); return true; } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return false; } public synchronized boolean unLock() { System.out.println(Thread.currentThread().getName() + "-->開始釋放鎖"); try { zooKeeper.delete(lockID, -1); System.out.println("節點" + lockID + "被釋放了"); return true; } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } return false; } public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(10); Random random = new Random(); for (int i = 0; i < 10; i++) { new Thread(() -> { DistributeLock lock = null; try { lock = new DistributeLock(); countDownLatch.countDown(); countDownLatch.await(); lock.lock(); Thread.sleep(random.nextInt(500)); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (lock != null) { lock.unLock(); } } }).start(); } } }
package zk.lock; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.util.concurrent.CountDownLatch; public class LockWatcher implements Watcher { private CountDownLatch countDownLatch; public LockWatcher(CountDownLatch latch) { this.countDownLatch = latch; } @Override public void process(WatchedEvent event) { if (event.getType() == Event.EventType.NodeDataChanged) { countDownLatch.countDown(); } } }
7*24小時可用, 99.999%可用
master-slave模式
slave監聽master節點,若是master節點掛掉,slave自動接替master,心跳機制維持,出現網絡異常,slave認爲master掛掉了,可能出現雙主節點的狀況,重複處理數據
使用zookeeper解決上述問題,往某一個節點註冊master節點,只有一個可以註冊成功,註冊成功的爲master,若是失去聯繫,節點會被刪除,不會出現腦裂問題
per實現原理講解
activeMQ、kafka、….
先進先出隊列
Barrier模式
在一個節點下只能容許多少數據,只有子節點達到必定數量,才執行
curator 提供應用場景的封裝
curator-reciples
master/leader選舉
分佈式鎖(讀鎖、寫鎖)
分佈式隊列
LeaderLatch
寫一個master
LeaderSelector
每個應用都寫一個臨時有序節點,根據最小的節點來得到優先權
curator 提供應用場景的封裝
curator-reciples
master/leader選舉
分佈式鎖(讀鎖、寫鎖)
分佈式隊列
…
LeaderLatch
寫一個master
LeaderSelector
每個應用都寫一個臨時有序節點,根據最小的節點來得到優先權
leader是zookeeper集羣的核心。
觀察zookeeper集羣中最新狀態的變化並將這些狀態同步到observer服務器上。
增長observer不影響集羣中事務處理能力,同時還能提高集羣的非事務處理能力
zookeeper通常是由 2n+1臺服務器組成
選舉算法:
leaderElection
AuthFastLeaderElection
FastLeaderElection
QuorumPeer startLeaderElection
源碼地址:https://github.com/apache/zoo...
須要的條件: jdk 1.7以上 、ant 、idea
FastLeaderElection
serverid : 在配置server集羣的時候,給定服務器的標識id(myid)
zxid : 服務器在運行時產生的數據ID, zxid的值越大,表示數據越新
Epoch: 選舉的輪數
server的狀態:Looking、 Following、Observering、Leading
第一次初始化啓動的時候: LOOKING
a) 判斷邏輯時鐘(Epoch) ,若是Epoch大於本身當前的Epoch,說明本身保存的Epoch是過時。更新Epoch,同時clear其餘服務器發送過來的選舉數據。判斷是否須要更新當前本身的選舉狀況
b) 若是Epoch小於目前的Epoch,說明對方的epoch過時了,也就意味着對方服務器的選舉輪數是過時的。這個時候,只須要講本身的信息發送給對方
c) 若是sid等於當前sid,根據規則來判斷是否有資格得到leader
接受到來自其餘服務器的投票後,針對每個投票,都須要將別人的投票和本身的投票進行對比,zxid,zxid最大的服務器優先
拜占庭問題
一組拜占庭將軍分別各率領一支軍隊共同圍困一座城市。爲了簡化問題,將各支軍隊的行動策略限定爲進攻或撤離兩種。由於部分軍隊進攻部分軍隊撤離可能會形成災難性後果,所以各位將軍必須經過投票來達成一致策略,即全部軍隊一塊兒進攻或全部軍隊一塊兒撤離。由於各位將軍分處城市不一樣方向,他們只能經過信使互相聯繫。在投票過程當中每位將軍都將本身投票給進攻仍是撤退的信息經過信使分別通知其餘全部將軍,這樣一來每位將軍根據本身的投票和其餘全部將軍送來的信息就能夠知道共同的投票結果而決定行動策略。系統的問題在於,將軍中可能出現叛徒,他們不只可能向較爲糟糕的策略投票,還可能選擇性地發送投票信息。假設有9位將軍投票,其中1名叛徒。8名忠誠的將軍中出現了4人投進攻,4人投撤離的狀況。這時候叛徒可能故意給4名投進攻的將領送信表示投票進攻,而給4名投撤離的將領送信表示投撤離。這樣一來在4名投進攻的將領看來,投票結果是5人投進攻,從而發起進攻;而在4名投撤離的將軍看來則是5人投撤離。這樣各支軍隊的一致協同就遭到了破壞。
因爲將軍之間須要經過信使通信,叛變將軍可能經過僞造信件來以其餘將軍的身份發送假投票。而即便在保證全部將軍忠誠的狀況下,也不能排除信使被敵人截殺,甚至被敵人間諜替換等狀況。所以很難經過保證人員可靠性及通信可靠性來解決問題。
假始那些忠誠(或是沒有出錯)的將軍仍然能經過多數決定來決定他們的戰略,便稱達到了拜占庭容錯。在此,票都會有一個默認值,若消息(票)沒有被收到,則使用此默認值來投票。
上述的故事映射到計算機系統裏,將軍便成了計算機,而信差就是通訊系統。雖然上述的問題涉及了電子化的決策支持與信息安全,卻沒辦法單純的用密碼學與數字簽名來解決。由於電路錯誤仍可能影響整個加密過程,這不是密碼學與數字簽名算法在解決的問題。所以計算機就有可能將錯誤的結果提交去,亦可能致使錯誤的決策。
paxos協議主要就是如何保證在分佈式環網絡環境下,各個服務器如何達成一致最終保證數據的一致性問題
ZAB協議,基於paxos協議的一個改進。
zab協議爲分佈式協調服務zookeeper專門設計的一種支持崩潰恢復的原子廣播協議
zookeeper並無徹底採用paxos算法, 而是採用zab Zookeeper atomic broadcast
leader節點主要負責把客戶端的事務請求轉化成一個事務提議(proposal),並分發給集羣中的全部follower節點
再等待全部follower節點的反饋。一旦超過半數服務器進行了正確的反饋,那麼leader就會commit這條消息
崩潰恢復
原子廣播
什麼狀況下zab協議會進入崩潰恢復模式(沒有接受到的數據進行同步)
zab協議進入崩潰恢復模式會作什麼
假設一個事務在leader服務器被提交了,而且已經有過半的follower返回了ack。 在leader節點把commit消息發送給follower機器以前leader服務器掛了怎麼辦
zab協議,必定須要保證已經被leader提交的事務也可以被全部follower提交
zab協議須要保證,在崩潰恢復過程當中跳過哪些已經被丟棄的事務
zookeeper數據模型
臨時節點(有序)、 持久化節點(有序)
臨時節點其餘節點也能看到
zookeeper是一個開源的分佈式協調框架; 數據發佈訂閱、負載均衡、集羣、master選舉。。。
原子性: 要麼同時成功、要麼同時失敗 (分佈式事務)
單一視圖: 不管客戶端鏈接到哪一個服務器,所看到的模型都是同樣
可靠性:一旦服務器端提交了一個事務而且得到了服務器端返回成功的標識,那麼這個事務所引發的服務器端的變動會一直保留
實時性: 近實時
zookeeper並非用來存儲數據的,經過監控數據狀態的變化,達到基於數據的集羣管理。分佈式
server.id=ip:port:port 第一個Port 數據同步通訊、 第二個port :leader選舉(3181)
id=myid (myid 參與leader選舉、 在整個集羣中表示惟一服務器的標識)
NOT_CONNECTED - > CONNECTING ->CONNECTED ->ClOSE
數據模型是一個樹形結構,最小的數據單元是ZNODE
臨時節點和持久化節點
臨時有序節點
持久化有序節點
Stat
cZxid = 0xb0000000f
ctime = Sun Aug 13 20:24:03 CST 2017
mZxid = 0xb0000000f
mtime = Sun Aug 13 20:24:03 CST 2017
pZxid = 0xb0000000f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x15dda30f72f0000
dataLength = 2
numChildren = 0
zab協議 : 若是客戶端發了一個事務請求給到leader, 而leader發送給各個follower之後,而且收到了ack,leader已經commit。 在準備ack給各個follower節點comit的時候,leader掛了,怎麼處理的。
EventyType
None 客戶端與服務器端成功創建會話
NodeCreated 節點建立
NodeDeleted 節點刪除
NodeDataChanged 數據變動:數據內容
NodeChildrenChanged 子節點發生變動: 子節點刪除、新增的時候,纔會觸發
watcher的特性
一次性觸發: 事件被處理一次後,會被移除,若是須要永久監聽,則須要反覆註冊
zkClient ( 永久監聽的封裝)
curator
java api的話, zk.exists , zk.getData 建立一個watcher監聽
zookeeper序列化使用的是Jute
保證存儲在zookeeper上的數據安全性問題
schema(ip/Digest/world/super)
受權對象(192.168.1.1/11 , root:root / world:anyone/ super)
內存數據和磁盤數據
zookeeper會定時把數據存儲在磁盤上。
DataDir = 存儲的是數據的快照
快照: 存儲某一個時刻全量的內存數據內容
DataLogDir 存儲事務日誌
zxid :服務器運行時產生的日誌id
log.zxid
查看事務日誌的命令
java -cp :/mic/data/program/zookeeper-3.4.10/lib/slf4j-api-1.6.1.jar:/mic/data/program/zookeeper-3.4.10/zookeeper-3.4.10.jar org.apache.zookeeper.server.LogFormatter log.200000001
zookeeper 有三種日誌
zookeeper.out //運行日誌
快照 存儲某一時刻的全量數據
事務日誌 事務操做的日誌記錄