【PHP小课堂】在PHP中使用Zookeeper

server/2024/12/19 3:04:16/

在PHP中使用Zookeeper

不知道大家对于 Zookeeper 的了解有多少,我在实际的项目中没有使用过,但是之前学过一点。因此,今天我们只来看看 PHP 中关于 Zookeeper 的扩展相关函数的使用,不会涉及更加深入的 Zookeeper 相关概念和细节的研究。

Zookeeper

还是先来简单地介绍一下 Zookeeper 吧。从名字可以看出,这个系统是 动物园管理员 的意思。为什么呢?因为在大数据相关的系统中,很多系统的标志都是各种动物,就像我们的 PHP 的标志也是一个大象。而 Zookeeper 的意思就是来管理这一大群动物的。它可以做到的功能包括:配置维护、域名服务、分布式同步、组服务等,并且提供了分布式独享锁、选举、队列的接口。

看着都是很高大上的功能吧。其实说白了,Zookeeper 就是一个分布式的应用程序协调服务。它提供了一个类似于 Linux 文件系统的目录树,这个目录树的每个结点都可以看成是一个 K/V 形式的 键值对 内容。可以生成永久的或者临时的、带序号或者不带序号的目录树。并且自带一个监听器,可以随时监听节点的变化方便从而实现对于系统的各种通知。

具体在应用方面,我们常见的服务注册、集群上下线通知、配置文件变更、分布式锁等相关功能都可以用它来实现。

具体的实现以及更详细的内容大家可以专门去找找 Zookeeper 相关的资料进行深入的学习。现在你可以去开三台虚拟机,然后搭建一个最简单的 Zookeeper 集群等着我们后面的使用了。具体的教程网上有很多,这里就不赘述了。

扩展安装

在 PHP 中使用 Zookeeper 扩展的安装会比较麻烦一点。我们还是先从 PECL 下载 Zookeeper 扩展。但是在 ./configuare 的时候会要求我们指定 libzookeeper 的路径。这个东西是需要编译源码安装在系统中的。

首先在https://downloads.apache.org/zookeeper/ 下载源码。我这里下载的是 3.5.7 版本。下载成功后解压,接下来就进行源码的安装,依次执行下面的命令。

yum install ant
# zookeeper 根目录下使用 ant 编译一下,否则下面无法 ./configure
ant compile_jute cd apache-zookeeper-3.5.7/zookeeper-client/zookeeper-client-c
./configure --prefix=/usr/local/zookeeper
make && make install

通过上述命令,Zookeeper 就被编译安装在了 /user/local/zookeeper 目录下。接下来进行 PHP 扩展安装,这里我下载的是 PECL 上的 1.0.0 版本。

cd zookeeper-1.0.0
./configure --prefix=/usr/local/zookeeper
make && make install

最后就是在 php.ini 文件中打开扩展。

[zookeeper]
extension=zookeeper

最后我们用 php -m 验证一下扩展安装成功,看到 zookeeper 出现了就可以了。

普通操作

普通的操作无非就是添加、修改和删除节点,以及建立与 Zookeeper 的集群连接。

建立连接

建立连接非常简单,只需要实例化 Zookeeper 对象,然后将服务器连接信息传递给构造参数即可。

$host = "192.168.10.102:2181,192.168.10.103:2181,192.168.10.104:2181";$zookeeper = new Zookeeper($host);

添加节点

添加节点的操作也比较简单,直接使用一个 create() 函数即可。

$acl = array(array('perms'  => Zookeeper::PERM_ALL,'scheme' => 'world','id'     => 'anyone',)
);
if(!$zookeeper->exists("/test")){$path = $zookeeper->create("/test", "ttt", $acl);echo $path; // /test
}

在这段测试代码中,我们还使用了一个 exists() 函数用于判断节点是否存在。如果节点不存在的话,我们就创建一个节点。create() 函数有四个参数,第一个是路径,第二个是路径的值,第三个是这个节点的权限。在这里我们给予的是全部权限 Zookeeper::PERM_ALL 。

如果要创建带序号的临时节点,我们可以使用 create() 的第四个参数。

echo $zookeeper->create("/test/app_", "appinfo" . date("Y-m-d H:i:s"), $acl, Zookeeper::EPHEMERAL|Zookeeper::SEQUENCE);
// /test/app_0000000077/test/show2

注意,Zookeeper 是可以生成 永久不带序号、永久带序号、临时不带序号、临时带序号 四种类型的节点的。直接根据第四个参数进行组合即可。当前这个测试代码的效果实际上就是 Zookeeper 命令行 create -s -e "/test/app_" "appinfo" 的效果。

接下来我们再创建两个节点用于后面的测试。

if(!$zookeeper->exists("/test/show1")){$path = $zookeeper->create("/test/show1", "show1", $acl);echo $path, PHP_EOL; // /test/show1
}if(!$zookeeper->exists("/test/show2")){$path = $zookeeper->create("/test/show2", "show1", $acl);echo $path, PHP_EOL; // /test/show2
}

获取信息

获取节点信息的函数也非常简单,就是一个 get() 函数。

echo $zookeeper->get("/test/show1"), PHP_EOL;
// show1

除了获取节点的值内容外,我们还可以获取节点的权限信息内容。

$stat = [];
$zookeeper->get("/test/show1", null, $stat);
print_r($stat);
// Array
// (
//     [czxid] => 21474836499
//     [mzxid] => 21474836731
//     [ctime] => 1638752001331
//     [mtime] => 1638760238766
//     [version] => 28
//     [cversion] => 0
//     [aversion] => 0
//     [ephemeralOwner] => 0
//     [dataLength] => 6
//     [numChildren] => 0
//     [pzxid] => 21474836499
// )print_r($zookeeper->getAcl("/test/show1"));
// Array
// (
//     [0] => Array
//         (
//             [czxid] => 21474836499
//             [mzxid] => 21474836731
//             [ctime] => 1638752001331
//             [mtime] => 1638760238766
//             [version] => 28
//             [cversion] => 0
//             [aversion] => 0
//             [ephemeralOwner] => 0
//             [dataLength] => 6
//             [numChildren] => 0
//             [pzxid] => 21474836499
//         )//     [1] => Array
//         (
//             [0] => Array
//                 (
//                     [perms] => 31
//                     [scheme] => world
//                     [id] => anyone
//                 )//         )// )

注意,get() 的第二个参数是定义监听器的,这个我们后面再讲,可以赋值为 null 就暂时不会监听。第三个参数是一个引用类型的参数,可以返回节点的状态内容信息。

getAcl() 是获取权限信息的函数,可以看到,它获取到的内容是两个数组,第一个数组里面是节点的状态信息,第二个数组里面是我们定义的节点权限信息。

最后我们再看一下获取子节点信息的函数 getChildren() 。

print_r($zookeeper->getChildren("/test"));
// Array
// (
//     [0] => show2
//     [1] => show1
//     [2] => app_0000000077
// )

返回的内容就是我们指定节点的子结点名称数组。

修改删除

修改节点使用的是 set() 函数,用于修改节点的值内容。

var_dump($zookeeper->set("/test/show1", "show11")); // bool(true)
echo $zookeeper->get("/test/show1"), PHP_EOL; // show11

另外我们也可以修改节点的权限信息,比如下面这样。

var_dump($zookeeper->setAcl("/test/show2", -1, [['perms'=>Zookeeper::PERM_READ, 'id'=>'anyone', 'scheme'=>'world']]));print_r($zookeeper->getAcl("/test/show2"));
//……
//             [0] => Array
//                 (
//                     [perms] => 1
//                     [scheme] => world
//                     [id] => anyone
//                 )
// ……$zookeeper->set("/test/show2", "show22");
// PHP Fatal error:  Uncaught ZookeeperAuthenticationException: not authenticated in /data/www/blog/zookeeper/1.php:50

使用 setAcl() 将这个节点的权限修改为只读之后,再使用 set() 去修改它的值,就会报出下面注释中的异常。

删除节点的操作是使用 delete() 函数。

var_dump($zookeeper->delete("/test/show2")); // bool(true)print_r($zookeeper->getChildren("/test"));
// Array
// (
//     [0] => show1
//     [1] => app_0000000077
// )

这里需要注意的是,节点的只读权限并不影响删除。

监听

PHP 的 Zookeeper 扩展默认提供了一个 setWatcher() 函数,不过我测不出来它的效果。

$zookeeper->setWatcher(function(){print_r(func_get_args());
});

上面的代码在运行的时候并没有执行,所以如果有使用过的小伙伴可以留言一起学习下哈,我们来看另外一种方式,也就是我们前面说过的 get() 函数可以添加监听器。

function watcher1($type, $state, $path){global $zookeeper;echo $type, ",", $state, ",", $path;switch ($type){case Zookeeper::CREATED_EVENT:echo"新建了目录:" . $path, PHP_EOL;break;case Zookeeper::DELETED_EVENT:echo"删除了目录:" . $path, PHP_EOL;break;case Zookeeper::CHANGED_EVENT:echo"修改了目录:" . $path, PHP_EOL;break;case Zookeeper::CHILD_EVENT:echo"修改了子目录:" . $path, PHP_EOL;break;default:echo"其它操作:" . $path, PHP_EOL;break;}$zookeeper->get("/test",'watcher1');
}$zookeeper->get("/test",'watcher1');while(1){sleep(2);};

在 Zookeeper 中,监听器只能注册一次监听一次,所以我们在监听器函数内部再调用了一次监听器用于下次的监听。监听回调函数包含三个参数,分别是 操作类型、状态值、路径 。我们的测试代码用于根据监听器的操作类型来返回对应的信息。别忘了,在文件底部还要加一个循环或者任何别的方式让代码保持挂载运行。

现在,你可以启动这个测试文件,然后进入 Zookeeper 命令行进行测试。比如我们输入下面的内容来修改节点值。

// 命令行
set /test "testaaa"// 脚本输出
3,3,/test修改了目录:/test

除了值的监听外,还可以监听子节点的变化。

function watcher($type, $state, $path){global $zookeeper;echo $type, ",", $state, ",", $path;switch ($type){case Zookeeper::CREATED_EVENT:echo"新建了目录:" . $path, PHP_EOL;break;case Zookeeper::DELETED_EVENT:echo"删除了目录:" . $path, PHP_EOL;break;case Zookeeper::CHANGED_EVENT:echo"修改了目录:" . $path, PHP_EOL;break;case Zookeeper::CHILD_EVENT:echo"修改了子目录:" . $path, PHP_EOL;break;default:echo"其它操作:" . $path, PHP_EOL;break;}$zookeeper->getChildren("/test",'watcher');
}$zookeeper->getChildren("/test",'watcher');

同样地,我们还是通过命令行来测试。

// 命令行
create -s -e /test/ooo "oo"// 脚本输出
4,3,/test修改了子目录:/test// 命令行
delete /test/ooo0000000082// 脚本输出
4,3,/test修改了子目录:/test

不管是服务注册、集群上下线、分布式锁等功能,其实都是在监听的基础上实现的。比如说集群管理,上线一台服务器就新建一个目录节点,然后其他服务器监听这个目录,有变化了就去查询一下,将新的服务器信息加入到本地配置中。

其他函数

除了上面这些主要的操作函数之外,还有一些辅助函数,大家了解一下即可。

// 当前客户端连接的 sessionid 等信息
var_dump($zookeeper->getClientId());
// array(2) {
//     [0]=>
//     int(144115198348099588)
//     [1]=>
//     string(17) "��슅��˟g��o�-"
//   }// ZookeeperConfig 配置对象
var_dump($zookeeper->getConfig());
// object(ZookeeperConfig)#3 (0) {
// }// 当前连接的过期时间
echo $zookeeper->getRecvTimeout(), PHP_EOL; // 10000// 连接状态,3 是已连接,和上面监听器中的 state 一样
echo $zookeeper->getState(), PHP_EOL; // 3// 检查当前连接状态是否可以恢复
var_dump($zookeeper->isRecoverable()); // bool(true)

总结

今天我们就是简单地学习了一下 PHP 中 Zookeeper 相关扩展的使用。在开头就说了,其实我并没有太多的实战经验,包括这个扩展,目前似乎也有直接可以在 Composer 中直接使用的 Zookeeper 组件了,并不需要这么费劲的安装原生编译的这种。所以,如果大家有相关经验或者资料的话,也希望多多留言,一起学习,共同进步。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/12/source/1.%E5%9C%A8PHP%E4%B8%AD%E4%BD%BF%E7%94%A8Zookeeper.php

参考文档:

https://www.php.net/manual/zh/book.zookeeper.php


http://www.ppmy.cn/server/151332.html

相关文章

【从零开始入门unity游戏开发之——C#篇12】新的引用类型——数组array

文章目录 一、数组(array)(一维数组)1、声明数组2、初始化数组3、访问数组元素4 、修改数组元素5、获取数组的长度6、遍历数组使用 for 循环:使用 foreach 循环: 7、数组方法排序:逆序&#xff…

9 OOM和JVM退出。OOM后JVM一定会退出吗?

首先我们把两个概念讲清楚 OOM是线程在申请堆内存,发现堆内存空间不足时候抛出的异常。 JVM退出的条件如下: java虚拟机在没有守护线程的时候会退出。守护线程是启动JVM的线程,服务于用户线程。 我们简单说下守护线程的功能: 1.日志的记录…

DeepSeek-AI 开源 DeepSeek-VL2 系列,采用专家混合(MoE)架构,重新定义视觉语言人工智能

将视觉与语言的智能融合,已经在视觉语言模型(Vision-Language Models,简称VLMs)领域实现了重大突破。这些模型致力于同步处理和解释视觉与文本数据,从而使得图像描述、视觉问题回答、光学字符识别(Optical …

ChatGPT崩溃引发行业震动:智能化之路需多元发展

今晨,当我如常打开ChatGPT,准备开始一天的工作时,却遭遇了令人措手不及的崩溃。起初,我还天真地以为这只是区域性的网络波动或是账号的小故障,于是费尽心思地清除浏览器缓存、cookies,甚至尝试更换区域设置…

大数据新视界 -- 大数据大厂之 Impala 性能飞跃:动态分区调整的策略与方法(上)(21 / 30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

ElasticSearch 数据聚合与运算

1、数据聚合 聚合(aggregations)可以让我们极其方便的实现数据的统计、分析和运算。实现这些统计功能的比数据库的 SQL 要方便的多,而且查询速度非常快,可以实现近实时搜索效果。 注意: 参加聚合的字段必须是 keywor…

【linux】shell(37)-脚本调试

1. 使用 Shell 调试选项 Shell 提供了多种调试选项,可以用于检查脚本的语法和执行过程。 1.1 -n 选项 作用:读取脚本但不执行,用于检查脚本的语法错误。 用法: bash -n script.sh示例: #!/bin/bash echo "H…

QT数据库(四):QSqlRelationalTableModel 类

关系数据库概念 例如下列departments、majors、studInfo 这 3 个数据表之间存在关系。 主键与外键 标记“**”的是主键字段,标记“*”的是外键字段。主键字段是一个数据表中表示记录唯一性的字段,例如 studInfo 数据表中的 studID 字段。外键字段是与其…