Zookeeper

简单介绍

Apache Zookeeper是开发和维护开源服务器的服务,它能够实现高度可靠的分布式协调。

安装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

安装Zookeeper C扩展支持


cd zookeeper-3.4.8/src/c
./configure --prefix=/usr/
make
make install

安装php的zookeeper扩展


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

启动zookeeper server


# {zookeeperserver}代表你zookeeper解压的目录 mkdir -p /project/zookeeper/demo/data #zoo.conf 是默认启动所加载的配置文件
cp /{zookeeperserver}/conf/zoo_sample.cfg /{zookeeperserver}/conf/zoo.cfg /{zookeeperserver}/bin/zkServer start

zookeeper client 测试


/{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

PHP下实践

zookeeper类信息


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

获取节点信息(Zookeeper::get)


# get.php $zoo = new Zookeeper('192.168.1.45:2181');
$r = $zoo->get( '/ztest');
var_dump($r);

获取节点信息(Zookeeper::get)并设置watcher


# 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

获取节点信息(Zookeeper::get) 结果


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]

php下获取子节点


# 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);

worker

一个利用顺序临时节点的leader迁移实现


# 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

这边发现一个类似bug问题


如果我们直接运行worker.php
在worker.php运行创建的临时顺序节点是不会被watcher到的
我们必须先首先创建好相关的节点再启动监控,不知道这里是不是php版本的zookeeper的bug
有了解的小伙伴可以告之下

Watcher通知状态与事件类型一览

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节点时触发

原文地址:https://segmentfault.com/a/1190000016173878

PHP和zookeeper结合实践的更多相关文章

  1. ZooKeeper场景实践:(6)集群监控和Master选举

    1. 集群机器监控 这通经常使用于那种对集群中机器状态,机器在线率有较高要求的场景,可以高速对集群中机器变化作出响应.这种场景中,往往有一个监控系统,实时检測集群机器是否存活. 利用ZooKeeper ...

  2. ZooKeeper场景实践:(2)集中式配置管理

    1. 基本介绍 在分布式的环境中,可能会有多个对等的程序读取相同的配置文件,程序能够部署在多台机器上,假设配置採用文件的话,则须要为部署该程序的机器也部署一个配置文件,一旦要改动配置的时候就会很麻烦, ...

  3. 13. ZooKeeper最佳实践

    以下列举了运行和管理ZooKeeper ensemble的一些最佳实践: ZooKeeper数据目录包含快照和事务日志文件.如果autopurge选项未启用,定期清理目录是一个好习惯.另外,管理员可能 ...

  4. zookeeper(3) zookeeper的实践及原理

    一.基于java API初探zookeeper的使用 (1)建立连接 public static void main(String[] args) { //NOT_CONNECTED-->CON ...

  5. zookeeper学习实践1-实现分布式锁

    引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...

  6. Docker部署Zookeeper部署实践(1)

    Zookeeper可提供的服务主要有:配置服务.名字服务.分布式同步.组服务等 1. 抓取Zookeeper镜像 命令:docker pull zookeeper 2. 将Zookeeper镜像保存为 ...

  7. 《从Paxos到Zookeeper:分布式一致性原理与实践》【PDF】下载

    内容简介 Paxos到Zookeeper分布式一致性原理与实践从分布式一致性的理论出发,向读者简要介绍几种典型的分布式一致性协议,以及解决分布式一致性问题的思路,其中重点讲解了Paxos和ZAB协议. ...

  8. Zookeeper 运维实践手册

    Zookeeper是一个高可用的分布式数据管理与协调框架,该框架能很好地保证分布式环境中数据一致性.一般用来实现服务发现(类似DNS),配置管理,分布式锁,leader选举等. 一.生产环境中Zook ...

  9. 从Paxos到Zookeeper 分布式一致性原理与实践读书心得

    一 本书作者介绍 此书名为从Paxos到ZooKeeper分布式一致性原理与实践,作者倪超,阿里巴巴集团高级研发工程师,国家认证系统分析师,毕业于杭州电子科技大学计算机系.2010年加入阿里巴巴中间件 ...

随机推荐

  1. 依据矩阵的二维相关系数进行OCR识别

    我想通过简单的模板匹配来进行图像识别. 把预处理好的字符图片,分别与A到Z的样本图片进行模板匹配. 结果最大的表明相关性最大,就能够识别字符图片了. 在实际应用中.我用了openCV的matchTem ...

  2. UIView 的图层关系

    个人认为用字母取代这样的比較好理解,.给新人学习 addSubview是一层一层往上加,新加的仅仅能放到父视图的最上层, insertSubView能够控制它加入到父视图的哪一层  A addSubv ...

  3. 【iOS】网络载入图片缓存与SDWebImage

    载入网络图片能够说是网络应用中必备的.假设单纯的去下载图片,而不去做多线程.缓存等技术去优化,载入图片时的效果与用户体验就会非常差. 一.自己实现载入图片的方法 tips: *iOS中全部网络訪问都是 ...

  4. Codeforces Round #277 (Div. 2)C.Palindrome Transformation 贪心

    C. Palindrome Transformation     Nam is playing with a string on his computer. The string consists o ...

  5. ES transport client使用

    ES transport client bulk insert 传输(transport)客户端 TransportClient利用transport模块远程连接一个elasticsearch集群.它 ...

  6. Win7系统专栏

    1.去掉Win7快捷方式小箭头的方法如下: 使用普通方法会使系统出现异常,比如开始菜单程序无法删除.收藏夹无法展开等,网上流传使用透明图标的方法会在快捷方式上留下一块黑痣,下面的方法是小君研究出来的, ...

  7. El和标准标签

    EL表达式针对于四大作用域:application,session,request,pagecontext(作用域由大倒小)${作用域获取内容的名字}是根据作用域最小的取,指定作用域${session ...

  8. NYOJ999 师傅又被妖怪抓走了

    只记得当下的眼疼 , ok 各种数据也试了 , 就是 他娘的不对 , 我也是醉了 . 也是日了最野的狗 附上日了哮天犬的代码 , 这个题 先放放, 一段时间后再试试 , 明天开始状态压缩吧 .为期两天 ...

  9. 【BZOJ1306】match循环赛

    预先警告:我的做法代码量比较大 看完题目后看到数据n<=8, 不难想到这题可以写深搜来做 分析 比如说以数据: 3 3 3 3 为例子, 进行了三场比赛:AB AC BC: 我们只要搜索每场比赛 ...

  10. Python迭代器(斐波拉切数列实例)

    将一个容器通过iter()函数处理后,就变成了迭代器.迭代器有2个魔法方法__iter__.__next__,一个迭代器必须实现__iter__,这个方法实际上是返回迭代器本身(return self ...