Apache Zookeeper是開發和維護開源服務器的服務,它可以實現高度可靠的分佈式協調。
wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz tar zxvf zookeeper-3.4.10.tar.gz
cd zookeeper-3.4.8/src/c ./configure --prefix=/usr/ make make install
wget https://pecl.php.net/get/zookeeper-0.6.2.tgz tar zxvf zookeeper-0.6.2.tgz cd zookeeper-0.6.2 phpize ./configure --with-libzookeeper-dir=/usr/ $ make $ make install
# {zookeeperserver}表明你zookeeper解壓的目錄 mkdir -p /project/zookeeper/demo/data #zoo.conf 是默認啓動所加載的配置文件 cp /{zookeeperserver}/conf/zoo_sample.cfg /{zookeeperserver}/conf/zoo.cfg /{zookeeperserver}/bin/zkServer start
/{zookeeperserver}/bin/zkCli.sh -server 127.0.0.1:2181 # [zk: 127.0.0.1:2181(CONNECTED) x] 客戶端鏈接信息shell [zk: 127.0.0.1:2181(CONNECTED) 3] ls / [zookeeper] [zk: 127.0.0.1:2181(CONNECTED) 4] create /test qkl001 Created /test [zk: 127.0.0.1:2181(CONNECTED) 5] create /zgq zgq002 Created /zgq [zk: 127.0.0.1:2181(CONNECTED) 6] ls / [zookeeper, test, zgq] [zk: 127.0.0.1:2181(CONNECTED) 5] delete /zgq Created /zgq [zk: 127.0.0.1:2181(CONNECTED) 7] quit
Zookeeper — Zookeeper類 -- Zookeeper::addAuth — 指定應用程序憑據 -- Zookeeper::connect — 建立與Zookeeper溝通的句柄 -- Zookeeper::__construct — 建立與Zookeeper溝通的句柄 -- Zookeeper::create — 同步建立節點 -- Zookeeper::delete — 同步刪除Zookeeper中的一個節點 -- Zookeeper::exists — 同步檢查Zookeeper節點的存在性 -- Zookeeper::get — 同步獲取與節點關聯的數據。 -- Zookeeper::getAcl — 同步地獲取與節點關聯的ACL。 -- Zookeeper::getChildren — 同步列出節點的子節點 -- Zookeeper::getClientId — 返回客戶端會話ID,僅在鏈接當前鏈接時纔有效(即最後觀察者狀態爲ZooOnCeleDelphi狀態) -- Zookeeper::getRecvTimeout — 返回此會話的超時,若是鏈接當前鏈接(只有上次觀察者狀態爲ZooOnCeleTytStand狀態)纔有效。此值可能在服務器從新鏈接後發生更改。 -- Zookeeper::getState — 獲取Zookeeper鏈接的狀態 -- Zookeeper::isRecoverable — 檢查當前的Zookeeper鏈接狀態是否能夠恢復 -- Zookeeper::set — 設置與節點關聯的數據 -- Zookeeper::setAcl — 同步設置與節點關聯的ACL -- Zookeeper::setDebugLevel — 設置庫的調試級別 -- Zookeeper::setDeterministicConnOrder — 啓用/禁用仲裁端點順序隨機化 -- Zookeeper::setLogStream — 設置庫用於日誌記錄的流 -- Zookeeper::setWatcher — 設置觀察函數
zkCli.cmd # output 表示鏈接成功 WatchedEvent state:SyncConnected type:None path:null [zk: localhost:2181(CONNECTED) 0] # 如下是操做命令 ls / create /ztest 1
# get.php $zoo = new Zookeeper('192.168.1.45:2181'); $r = $zoo->get( '/ztest'); var_dump($r);
# get.php class ZookeeperDemo extends Zookeeper { public function watcher($type, $state, $key) { var_dump($type); var_dump($state); var_dump($key); if ($type == 3) { var_dump($this->get('/zgetw')); // Watcher gets consumed so we need to set a new one $this->get('/zgetw', array($this, 'watcher')); } } } $zoo = new ZookeeperDemo('192.168.1.45:2181'); $zoo->get('/zgetw', [$zoo, 'watcher']); while (true) { echo '.'; sleep(1); }
set /ztest 2 set /ztest 3 set /ztest 5
php get.php #output ......int(3) int(3) string(6) "/ztest" string(1) "2" ..int(3) int(3) string(6) "/ztest" string(1) "3" ..int(3) int(3) string(6) "/ztest" string(1) "4" .int(3) int(3) string(6) "/ztest" string(1) "5"
create /ztest/child1 c1 # output: Created /ztest/child1 create /ztest/child2 c2 # output: Created /ztest/child2 ls /ztest # output: [child2, child1]
# getChild.php $zookeeper = new Zookeeper('192.168.1.45:2181'); $path = '/ztest'; $r = $zookeeper->getchildren($path); if (!empty($r)) { foreach ($r as $c) { var_dump($c); } } # php getChild.php string(6) "child2" string(6) "child1"
[zk: localhost:2181(CONNECTED) 22] create /stest 1 Created /stest [zk: localhost:2181(CONNECTED) 23] create -s /stest/seq 1 Created /stest/seq0000000000 [zk: localhost:2181(CONNECTED) 24] create -s /stest/seq 1 Created /stest/seq0000000001 [zk: localhost:2181(CONNECTED) 25] create -s /stest/seq 1 Created /stest/seq0000000002 [zk: localhost:2181(CONNECTED) 26] create -s /stest/seq 1 Created /stest/seq0000000003 [zk: localhost:2181(CONNECTED) 27] create -s /stest/seq 1 Created /stest/seq0000000004 [zk: localhost:2181(CONNECTED) 28] create -s /stest/seq 1 Created /stest/seq0000000005 [zk: localhost:2181(CONNECTED) 29] create -s /stest/seq 1 Created /stest/seq0000000006 [zk: localhost:2181(CONNECTED) 30] create -s /stest/seq 1 Created /stest/seq0000000007 [zk: localhost:2181(CONNECTED) 31] create -s /stest/seq 2 Created /stest/seq0000000008 [zk: localhost:2181(CONNECTED) 32] create -s /stest/seq 2 Created /stest/seq0000000009 [zk: localhost:2181(CONNECTED) 33] create -s /stest/seq 2 Created /stest/seq0000000010 [zk: localhost:2181(CONNECTED) 34] create -s /stest/seq 2 Created /stest/seq0000000011 [zk: localhost:2181(CONNECTED) 35] create -s /stest/seq 2 Created /stest/seq0000000012 [zk: localhost:2181(CONNECTED) 36] create -s /stest/seq 2 Created /stest/seq0000000013 [zk: localhost:2181(CONNECTED) 37] create -s /stest/seq 2 Created /stest/seq0000000014
$zookeeper = new Zookeeper('192.168.1.45:2181'); $aclArray = array( array( 'perms' => Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone', ) ); $path = '/t3'; //ZOO_EPHEMERAL = 1 //ZOO_SEQUENCE = 2 //這裏這裏的flag=NULL,flag=0 表示建立永久節點,=1建立臨時節點,=2建立seq 順序節點 $realPath = $zookeeper->create($path, null, $aclArray, 1); var_dump($realPath);
一個利用順序臨時節點的leader遷移實現php
# worker.php <?php /** * Created by PhpStorm. * User: qkl * Date: 2018/8/27 * Time: 14:25 */ class Worker { const CONTAINER = '/worker_test'; protected $acl = [ [ 'perms' => Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone' ] ]; private $isLeader = false; private $znode = ''; private $prevNode = ''; public function __construct($host = '', $watcher_cb = null, $recv_timeout = 10000) { $this->zk = new Zookeeper($host, $watcher_cb, $recv_timeout); } public function register() { if (!$this->zk->exists(self::CONTAINER)) { $this->zk->create(self::CONTAINER, null, $this->acl); } # 建立一個臨時的順序節點 $this->znode = $this->zk->create(self::CONTAINER . '/w-', null, $this->acl, Zookeeper::EPHEMERAL | Zookeeper::SEQUENCE); //獲取順序節點名,去除父路徑 $this->znode = str_replace(self::CONTAINER . '/', '', $this->znode); printf("我建立了節點: %s\n", self::CONTAINER . '/' . $this->znode); $this->prevNode = $this->getPrev(); if (is_null($this->prevNode)) { $this->setLeader(true); } else { $this->zk->get(self::CONTAINER . "/" . $this->prevNode, [$this, 'watcher']); } } public function getPrev() { $workers = $this->zk->getChildren(self::CONTAINER); sort($workers); $size = count($workers); for ($i = 0; $i < $size; $i++) { if ($this->znode == $workers[$i] && $i > 0) { return $workers[$i - 1]; } } return null; } public function watcher($type, $state, $key) { $prevNode = $this->prevNode; printf("watchNode-getPrevious:%s\n", self::CONTAINER . "/" . $prevNode); if (!is_null($prevNode)) { if ($type == 2) { printf($prevNode . " had deleted\n"); } if ($type == 3) { printf("我從新監控節點:%s\n", self::CONTAINER . "/" . $prevNode); $this->zk->get(self::CONTAINER . "/" . $prevNode, [$this, 'watcher']); } } } public function isLeader() { return $this->isLeader; } public function setLeader($flag) { $this->isLeader = $flag; } public function run() { $this->register(); while (true) { if ($this->isLeader()) { $this->leaderJob(); } else { $this->watcherJob(); } sleep(2); } } public function leaderJob() { echo "如今我是Leader\n"; } public function watcherJob() { echo "我在監控:".(self::CONTAINER . "/" . $this->prevNode)."\n"; } } $worker = new Worker('192.168.1.45:2181'); $worker->run();
# 終端1 php worker.php # 終端2 php worker.php # 終端3 php worker.php # 此處運行不會被節點變化不會被監控到
# zkCli create /worker_test/w- /worker_test/0000000020 php worker.php # output 我建立了節點: /worker_test/w-0000000022 我在監控:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 watchNode-getPrevious:/worker_test/w-0000000020 我從新監控節點:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 watchNode-getPrevious:/worker_test/w-0000000020 我從新監控節點:/worker_test/w-0000000020 2018-08-28 02:11:46,684:2486(0x7f28ed0a1700):ZOO_WARN@zookeeper_interest@1570: Exceeded deadline by 15ms 我在監控:/worker_test/w-0000000020 我在監控:/worker_test/w-0000000020 watchNode-getPrevious:/worker_test/w-0000000020 w-0000000020 had deleted 我在監控:/worker_test/w-0000000020
若是咱們直接運行worker.php 在worker.php運行建立的臨時順序節點是不會被watcher到的 咱們必須先首先建立好相關的節點再啓動監控,不知道這裏是否是php版本的zookeeper的bug 有了解的小夥伴能夠告之下
ZOO_CREATED_EVENT(value=1):節點建立事件,須要watch一個不存在的節點,當節點被建立時觸發,此watch經過zoo_exists()設置
ZOO_DELETED_EVENT(value=2):節點刪除事件,此watch經過zoo_exists()或zoo_get()設置
ZOO_CHANGED_EVENT(value=3):節點數據改變事件,此watch經過zoo_exists()或zoo_get()設置
ZOO_CHILD_EVENT(value=4):子節點列表改變事件,此watch經過zoo_get_children()或zoo_get_children2()設置
ZOO_SESSION_EVENT(value=-1):會話事件,客戶端與服務端斷開或重連時觸發
ZOO_NOTWATCHING_EVENT(value=-2):watch移除事件,服務端出於某些緣由再也不爲客戶端watch節點時觸發node