博客
关于我
php zookeeper实现分布式锁
阅读量:793 次
发布时间:2023-02-27

本文共 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的临时顺序节点机制被充分利用,确保了锁的释放自动化,减少了“羊群效应”。

转载地址:http://dgvfk.baihongyu.com/

你可能感兴趣的文章
oracle中的大小写、字符、dual、数字、处理、日期、函数、显/隐式、时间、条件表达式case、decode、to_date、to_char、sysdate
查看>>
Oracle修改字段类型
查看>>
oracle典型安装失败,安装oracle 10失败
查看>>
Oracle分析函数之LEAD和LAG
查看>>
Oracle和SQL server的数据类型比较
查看>>
Oracle用游标删除重复数据
查看>>
Oracle监听配置、数据库实例配置等
查看>>
oracle系统 介绍,ORACLE数据库管理系统介绍
查看>>
oracle获取数据库表、字段、注释、约束等
查看>>
Oracle计划将ZGC项目提交给OpenJDK
查看>>
Oracle闪回技术(Flashback)
查看>>
oracle零碎要点---ip地址问题,服务问题,系统默认密码问题
查看>>
oracle零碎要点---oracle em的web访问地址忘了
查看>>
Oracle零碎要点---多表联合查询,收集数据库基本资料
查看>>
Oracle静默安装
查看>>
Oracle面试题:Oracle中truncate和delete的区别
查看>>
ThreadLocal线程内部存储类
查看>>
thinkphp 常用SQL执行语句总结
查看>>
Oracle:ORA-00911: 无效字符
查看>>
Text-to-Image with Diffusion models的巅峰之作:深入解读 DALL·E 2
查看>>