详解PHP中互斥锁库hyperf-wise-locksmith的使用

 更新时间:2024年11月28日 10:48:38   作者:左诗右码  
hyperf-wise-locksmith 库作为 Hyperf 框架中的一员,提供了一个高效、简洁的互斥锁解决方案,下面就跟随小编一起了解下它的具体使用吧

在分布式系统中,如何确保多台机器之间不会产生竞争条件,是一个常见且重要的问题。hyperf-wise-locksmith 库作为 Hyperf 框架中的一员,提供了一个高效、简洁的互斥锁解决方案。

本文将带你了解这个库的安装、特性、基本与高级功能,并结合实际应用场景,展示其在项目中的应用。

hyperf-wise-locksmith 库简介

hyperf-wise-locksmith 是一个适配 Hyperf 框架的互斥锁库,它基于 pudongping/wise-locksmith 库构建。它可以帮助我们在分布式环境下进行锁的管理,确保同一时刻只有一个进程能够操作某些共享资源,从而避免数据的竞争和不一致问题。

安装

要在你的 Hyperf 项目中使用 hyperf-wise-locksmith,你需要通过 Composer 进行安装:

composer require pudongping/hyperf-wise-locksmith -vvv

确保你的环境满足以下要求:

  • PHP >= 8.0
  • hyperf ~3.0.0。

特性

hyperf-wise-locksmith 提供了多种锁机制,包括文件锁分布式锁红锁协程级别的互斥锁。这些锁机制可以帮助开发者在不同的场景下保护共享资源,避免竞态条件。

基本功能

文件锁(flock)

文件锁是一种简单的锁机制,它依赖于文件系统。以下是一个使用文件锁的示例:

    private function flock(float $amount)
    {
        $path = BASE_PATH . '/runtime/alex.lock.cache';
        $fileHandler = fopen($path, 'a+');
        // fwrite($fileHandler, sprintf("%s - %s \r\n", 'Locked', microtime()));

        $res = $this->locker->flock($fileHandler, function () use ($amount) {
            return $this->deductBalance($amount);
        });
        return $res;
    }

分布式锁(redisLock)

分布式锁适用于分布式系统,它依赖于 Redis。以下是一个使用分布式锁的示例:

    private function redisLock(float $amount)
    {
        $res = $this->locker->redisLock('redisLock', function () use ($amount) {
            return $this->deductBalance($amount);
        }, 10);
        return $res;
    }

高级功能

红锁(redLock)

红锁是一种更安全的分布式锁实现,它需要多个 Redis 实例。以下是一个使用红锁的示例:

    private function redLock(float $amount)
    {
        $res = $this->locker->redLock('redLock', function () use ($amount) {
            return $this->deductBalance($amount);
        }, 10);
        return $res;
    }

协程级别的互斥锁(channelLock)

协程级别的互斥锁适用于协程环境,它提供了一种轻量级的锁机制。以下是一个使用协程锁的示例:

    private function channelLock(float $amount)
    {
        $res = $this->locker->channelLock('channelLock', function () use ($amount) {
            return $this->deductBalance($amount);
        });
        return $res;
    }

实际应用场景

假设我们有一个在线支付系统,需要在多个请求中扣减用户的余额。如果不使用互斥锁,可能会导致超扣或扣减失败。使用 hyperf-wise-locksmith 库,我们可以确保每次扣减操作都是原子性的。

代码示例

以下是一个扣减用户余额的示例,使用了 hyperf-wise-locksmith 库:

<?php
declare(strict_types=1);

namespace App\Services;

use Hyperf\Contract\StdoutLoggerInterface;
use Pudongping\HyperfWiseLocksmith\Locker;
use Pudongping\WiseLocksmith\Exception\WiseLocksmithException;
use Pudongping\WiseLocksmith\Support\Swoole\SwooleEngine;
use Throwable;

class AccountBalanceService
{

    /**
     * 用户账户初始余额
     *
     * @var float|int
     */
    private float|int $balance = 10;

    public function __construct(
        private StdoutLoggerInterface $logger,
        private Locker                $locker
    ) {
        $this->locker->setLogger($logger);
    }

    private function deductBalance(float|int $amount)
    {
        if ($this->balance >= $amount) {
            // 模拟业务处理耗时
            usleep(500 * 1000);
            $this->balance -= $amount;
        }

        return $this->balance;
    }

    /**
     * @return float
     */
    private function getBalance(): float
    {
        return $this->balance;
    }

    public function runLock(int $i, string $type, float $amount)
    {
        try {
            $start = microtime(true);

            switch ($type) {
                case 'flock':
                    $this->flock($amount);
                    break;
                case 'redisLock':
                    $this->redisLock($amount);
                    break;
                case 'redLock':
                    $this->redLock($amount);
                    break;
                case 'channelLock':
                    $this->channelLock($amount);
                    break;
                case 'noMutex':
                default:
                    $this->deductBalance($amount);
                    break;
            }

            $balance = $this->getBalance();
            $id = SwooleEngine::id();
            $cost = microtime(true) - $start;
            $this->logger->notice('[{type} {cost}] ==> [{i}<=>{id}] ==> 当前用户的余额为:{balance}', compact('type', 'i', 'balance', 'id', 'cost'));

            return $balance;
        } catch (WiseLocksmithException|Throwable $e) {
            return sprintf('Err Msg: %s ====> %s', $e, $e->getPrevious());
        }
    }

}

然后我们再写一个控制器进行调用

<?php

declare(strict_types=1);

namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use App\Services\AccountBalanceService;
use Hyperf\Coroutine\Parallel;
use function \Hyperf\Support\make;

#[AutoController]
class BalanceController extends AbstractController
{

    // curl '127.0.0.1:9511/balance/consumer?type=noMutex'
    public function consumer()
    {
        $type = $this->request->input('type', 'noMutex');
        $amount = (float)$this->request->input('amount', 1);

        $parallel = new Parallel();
        $balance = make(AccountBalanceService::class);

        // 模拟 20 个并发
        for ($i = 1; $i <= 20; $i++) {
            $parallel->add(function () use ($balance, $i, $type, $amount) {
                return $balance->runLock($i, $type, $amount);
            }, $i);
        }

        $result = $parallel->wait();

        return $this->response->json($result);
    }

}

当我们访问 /balance/consumer?type=noMutex 地址时,我们可以看到用户的余额会被扣成负数,这明显不符合逻辑。 然而当我们访问下面几个地址时,我们可以看到用户余额不会被扣成负数,则说明很好的保护了竞态下的共享资源的准确性。

  • /balance/consumer?type=flock :文件锁
  • /balance/consumer?type=redisLock :分布式锁
  • /balance/consumer?type=redLock :红锁
  • /balance/consumer?type=channelLock :协程级别的互斥锁

注意

关于使用到 redisLockredLock 时:

  • 使用 redisLock 默认采用的 config/autoload/redis.php 配置文件中的第一个 key 配置 redis 实例(即 default)。可按需传入第 4 个参数 string|null $redisPoolName 进行重新指定。
  • 使用 redLock 默认采用的 config/autoload/redis.php 配置文件中的所有 key 对应的配置 redis 实例。可按需传入第 4 个参数 ?array $redisPoolNames = null 进行重新指定。

文档

详细文档可见 pudongping/wise-locksmith

结语

hyperf-wise-locksmith 库为 Hyperf 框架的开发者提供了强大的互斥锁功能,可以帮助我们在高并发场景下保护共享资源。

通过本文的介绍,希望你能对 hyperf-wise-locksmith 有一个全面的了解,并在你的项目中灵活运用。如果你觉得这个库对你有帮助,希望你可以帮忙点个 Star 哟~

以上就是详解PHP中互斥锁库hyperf-wise-locksmith的使用的详细内容,更多关于PHP hyperf-wise-locksmith的资料请关注脚本之家其它相关文章!

相关文章

  • php中Workerman框架实例讲解

    php中Workerman框架实例讲解

    在本篇文章里小编给大家分享了一篇关于php中Workerman框架实例讲解内容,对此有兴趣的朋友们可以学习参考下。
    2021-02-02
  • php将字符串全部转换成大写或者小写的方法

    php将字符串全部转换成大写或者小写的方法

    这篇文章主要介绍了php将字符串全部转换成大写或者小写的方法,涉及php中strtolower与strtoupper等函数的使用技巧,需要的朋友可以参考下
    2015-03-03
  • php变量与JS变量实现不通过跳转直接交互的方法

    php变量与JS变量实现不通过跳转直接交互的方法

    这篇文章主要介绍了php变量与JS变量实现不通过跳转直接交互的方法,涉及php与javascript变量传输及代码混编相关操作技巧,需要的朋友可以参考下
    2017-08-08
  • php实现的递归提成方案实例

    php实现的递归提成方案实例

    这篇文章主要介绍了php实现的递归提成方案,以完整实例形式较为详细的分析了php提成方案的数据库设计及功能实现技巧,涉及PHP数学运算的相关技巧,需要的朋友可以参考下
    2015-11-11
  • 帝国cms目录结构分享

    帝国cms目录结构分享

    本文给大家分享的是一款非常不错的也很实用的php的内容发布系统--帝国CMS的目录结构,方便大家进行二次开发。
    2015-07-07
  • PHP实现APP微信支付的实例讲解

    PHP实现APP微信支付的实例讲解

    下面小编就为大家分享一篇PHP实现APP微信支付的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • PHP中将字符串转化为整数(int) intval() printf() 性能测试

    PHP中将字符串转化为整数(int) intval() printf() 性能测试

    早在Sql注入横行的前几年,字符串转化为整数就已经被列为每个web程序必备的操作了。web程序将get或post来的id、整数等值强制经过转化函数转化为整数,过滤掉危险字符,尽可能降低系统本身被Sql注入的可能性
    2012-03-03
  • php实现文件下载代码分享

    php实现文件下载代码分享

    php实现文件下载我们需要用到header函数来发送相关信息给客户端浏览器,同时再结合filesize函数来读取文件大小并进行下载操作,下面我们一起来看看相关例子。
    2014-08-08
  • 浅谈PHP SHA1withRSA加密生成签名及验签

    浅谈PHP SHA1withRSA加密生成签名及验签

    这篇文章主要介绍了PHP SHA1withRSA加密生成签名及验签,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • PHP排序算法之简单选择排序(Simple Selection Sort)实例分析

    PHP排序算法之简单选择排序(Simple Selection Sort)实例分析

    这篇文章主要介绍了PHP排序算法之简单选择排序(Simple Selection Sort),结合实例形式较为详细的分析了简单选择排序算法的原理与相关实现技巧,需要的朋友可以参考下
    2018-04-04

最新评论