php zookeeper实现分布式锁
发布日期:2025-05-02 23:23:28 浏览次数:9 分类:精选文章

本文共 5302 字,大约阅读时间需要 17 分钟。

PHP实现Zookeeper分布式锁

分布式锁原理

Zookeeper作为一款开源的分布式协调服务,能够为我们提供强一致性的数据管理能力。分布式锁可以分为两类:一种是保持独占性锁,另一种是控制时序锁。

保持独占性锁

这种类型的锁确保在所有客户端中,最终只有一个客户端能够成功获取锁。实现方法是将Zookeeper的一个节点视为锁。所有客户端尝试在/distribute_lock节点下创建临时子节点,最终成功创建的客户端即为锁的持有者。

控制时序锁

这种类型的锁确保所有客户端都能按照一定的顺序获取锁。实现方法是将/distribute_lock节点作为父节点,客户端在其下创建临时顺序节点。Zookeeper会根据顺序保证子节点的唯一性,从而形成全局时序。

获取锁的实现方法

方法一:创建临时节点

所有客户端尝试在/distribute_lock/lock节点下创建临时子节点。Zookeeper保证在所有客户端中,只有一个客户端能够成功创建节点。成功创建节点的客户端即为锁的持有者。未成功创建的客户端需要注册监听器,实时监控/distribute_lock/lock节点的状态变化。这种方法虽然简单,但容易出现“羊群效应”,影响性能。

方法二:创建临时顺序节点

这种方法更为合理。每个试图加锁的客户端都创建一个临时顺序节点。Zookeeper保证节点的唯一性和顺序性。客户端需要获取所有子节点,按顺序排列,判断最小节点是否为自己。如果不是,则获取上一个节点并注册监听器,等待通知。

代码实现

核心类

class zkCli {    protected static $zk;    protected static $myNode;    protected static $isNotifyed;    protected static $root;    public static function getZkInstance($conf, $root) {        try {            if (isset(self::$zk)) {                return self::$zk;            }            $zk = new \Zookeeper($conf['host'] . ':' . $conf['port']);            if (!$zk) {                throw new \Exception('connect zookeeper error');            }            self::$zk = $zk;            self::$root = $root;            return $zk;        } catch (\ZookeeperException $e) {            die($e->getMessage());        } catch (\Exception $e) {            die($e->getMessage());        }    }    public static function tryGetDistributedLock($lockKey, $value) {        try {            self::createRootPath($value);            self::createSubPath(self::$root . $lockKey, $value);            return self::getLock();        } catch (\ZookeeperException $e) {            return false;        } catch (\Exception $e) {            return false;        }    }    public static function releaseDistributedLock() {        if (self::$zk->delete(self::$myNode)) {            return true;        } else {            return false;        }    }    private static function createRootPath($value) {        $aclArray = [            ['perms' => Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone'],        ];        if (false == self::$zk->exists(self::$root)) {            $result = self::$zk->create(self::$root, $value, $aclArray);            if (false == $result) {                throw new \Exception('create ' . self::$root . ' fail');            }        }        return true;    }    private static function createSubPath($path, $value) {        $aclArray = [            ['perms' => Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone'],        ];        self::$myNode = self::$zk->create($path, $value, $aclArray, Zookeeper::EPHEMERAL | Zookeeper::SEQUENCE);        if (false == self::$myNode) {            throw new \Exception('create -s -e ' . $path . ' fail');        }        return true;    }    private static function getLock() {        if (self::checkMyNodeOrBefore()) {            return true;        } else {            self::$isNotifyed = false;            $result = self::$zk->get(self::$myNode, [zkCli::class, 'watcher']);            while (!$result) {                $res1 = self::checkMyNodeOrBefore();                if ($res1 === true) {                    return true;                } else {                    $result = self::$zk->get($res1, [zkCli::class, 'watcher']);                }            }            while (!self::$isNotifyed) {                echo '.';                usleep(500000);            }            return true;        }    }    private static function checkMyNodeOrBefore() {        $list = self::$zk->getChildren(self::$root);        sort($list);        array_walk($list, function($val) use ($root) {            $val = $root . '/' . $val;        });        if ($list[0] == self::$myNode) {            echo 'get lock node ' . self::$myNode . '....';            return true;        } else {            $index = array_search(self::$myNode, $list);            $before = $list[$index - 1];            echo 'before node ' . $before . '.........';            return $before;        }    }    public static function watcher($type, $state, $key) {        echo $key . ' notifyed ....';        self::$isNotifyed = true;        self::getLock();    }}

使用示例

function zkLock($resourceId) {    $conf = ['host' => '127.0.0.1', 'port' => 2181];    $root = '/lockKey_' . $resourceId;    $lockKey = '/lock_';    $value = 'a';    $client = zkCli::getZkInstance($conf, $root);    $re = zkCli::tryGetDistributedLock($lockKey, $value);    if ($re) {        echo 'get lock success';    } else {        echo 'get lock fail';        return;    }    try {        doSomething();    } catch (\Exception $e) {        echo $e->getMessage() . PHP_EOL;    } finally {        $re = zkCli::releaseDistributedLock();        if ($re) {            echo 'release lock success';        } else {            echo 'release lock fail';        }        return;    }}function doSomething() {    $n = rand(1, 20);    switch ($n) {        case 1:            sleep(15);            break;        case 2:            throw new \Exception('system throw message...');            break;        case 3:            die('system crashed...');            break;        default:            sleep(13);            break;    }}// 执行 zkLock(0);zkLock(0);

总结

通过以上实现,我们可以利用Zookeeper的强一致性特性,实现分布式锁。两种方法都有其优缺点,方法二虽然麻烦一些,但更为合理。代码实现中,Zookeeper的临时顺序节点机制被充分利用,确保了锁的释放自动化,减少了“羊群效应”。

上一篇:PHP 中 this,self,parent 的区别、用法
下一篇:PHP XSS攻击防范--如何过滤用户输入

发表评论

最新留言

表示我来过!
[***.240.166.169]2026年05月20日 17时22分18秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章