在《PHP Zookeeper你需要知道的细节》(以下称为php_zk)一章中我们提出了问题,并且通过分析PHP-ZooKeeper源码找出了问题的原因,但是并没有给出解决方法。本章我们就来看一看解决的办法。
解决的办法分为两种:
一是修改PHP-ZooKeeper源码,重新编译安装
二是修改php代码
下面我们先来看第一种方式。
修改PHP-ZooKeeper源码
在php_zk一章中我们看到,其实在znode节点值为NULL的时候,使用php-zookeeper的get()方法能监听znode节点,但是并不能成功调用回调函数。原因就在于get()这个函数底层实现是当znode值为null的时候调用的函数如下
status = zoo_exists(i_obj->zk, path, 1, &stat);
但是我们可以看一下当znode有值的时候,它会调用到下面的函数
status = zoo_wget(i_obj->zk, path, (fci.size != 0) ? php_zk_watcher_marshal : NULL,
cb_data, buffer, &length, &stat);
所以我们分析php_zk_watcher_marshal是解析回调函数的,而在zoo_exists()中第三个参数为1,因此要想解决问题,必须要在zoo_exists()这个函数上找突破。继续进入zoo_exists()函数底层的定义发现其实该函数的实现也很简单
int zoo_exists(zhandle_t *zh, const char *path, int watch, struct Stat *stat)
{
return zoo_wexists(zh,path,watch?zh->watcher:0,zh->context,stat);
}
在它的底层又调用了zoo_wexists()函数。所以说我们完全可以在get()函数定义中将zoo_exists()函数改成zoo_wexists()。示例如下
if (max_size <= 0) {
status =zoo_wexists(i_obj->zk,path,(fci.size!=0)?php_zk_watcher_marshal:NULL,cb_data,&stat);
if (status != ZOK) {
php_cb_data_destroy(&cb_data);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "error: %s", zerror(status));
return;
}
length = stat.dataLength;
} else {
length = max_size;
}
这样一来,回调函数就会被执行了。
保存以后,重新编译安装就可以了
# ./configure --with-libzookeeper-dir=/opt/zk_c/ --with-php-config=/usr/local/php5/bin/php-config
# make
# make test
# make install
因为之前已经在php.ini中添加了zookeeper的配置,所以这里不用在进行任何配置。
完成以后再次按照php_zk文章中进行php代码的运行步骤就会发现能正常的进行新的leader的选举了。
修改php代码
虽然能修改PHP-ZooKeeper的源码来解决问题,但是没有十足的把握还是不要修改源码,因为这可能会影响到其它函数的使用。所以这里我们可以在php代码层来进行修改。
我们在php_zk一章中看到,在进行/cluster子节点的创建中使用的是下面的代码
$this->znode = $this->create(self::CONTAINER . '/w-', null, $this->acl, Zookeeper::EPHEMERAL | Zookeeper::SEQUENCE);
在$this->create()函数中,第三个参数是znode的值,这里我们默认为null。如果我们给他一个不为null的默认值的话,这个问题就迎刃而解了。如下示例
$this->znode = $this->create(self::CONTAINER . '/w-', '1', $this->acl, Zookeeper::EPHEMERAL | Zookeeper::SEQUENCE);
当然我们这里为了测试,只是简单的给了一个1。在实际场景中在创建节点的时候会根据实际应用来给出默认值,这里就暂且使用1来测试。
修改以后再次按照之前的步骤运行——不用在zookeeper客户端单独进行值的设置——就会发现也是能重新选出leader的。
以上两种方式我都是经过代码测试的,实践证明其可行。但是在实际中应该怎样去使用PHP-ZooKeeper都要根据场景去定。我这里只是抛砖引玉,指出在学习中的一些问题,希望能帮到各位。