浦东平 / hyperf-wise-locksmith
Hyperf 框架的互斥库提供者,旨在实现高并发场景下 PHP 代码的序列化执行。
2.0.0
2023-08-24 05:56 UTC
Requires
- php: >=8.0
- hyperf/logger: ~3.0.0
- hyperf/redis: ~3.0.0
- pudongping/wise-locksmith: ^1.0
This package is auto-updated.
Last update: 2024-09-24 08:17:51 UTC
README
需求 | 安装 | 分支或标签 | 快速入门 | 注意 | 文档 | 贡献 | 许可
hyperf-wise-locksmith
英文 | 中文
🔒 Hyperf 框架的互斥库提供者,旨在实现高并发场景下 PHP 代码的序列化执行。此库基于 pudongping/wise-locksmith。
需求
- PHP >= 8.0
- hyperf ~3.0.0
安装
composer require pudongping/hyperf-wise-locksmith:^2.0 -vvv
分支或标签
分支
- 2.2: 适用于 hyperf 2.2
- 3.0: 适用于 hyperf 3.0
标签
- 1.0.x: 适用于 hyperf 2.2
- 2.0.x: 适用于 hyperf 3.0
快速入门
以下将提供一个高并发场景中扣除用户余额的示例,以演示此库的功能和用法。
创建 app\Controller\BalanceController.php
文件并写入以下代码
<?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); } }
接下来,创建 app\Services\AccountBalanceService.php
文件并写入以下代码
<?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()); } } 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; } private function redisLock(float $amount) { $res = $this->locker->redisLock('redisLock', function () use ($amount) { return $this->deductBalance($amount); }, 10); return $res; } private function redLock(float $amount) { $res = $this->locker->redLock('redLock', function () use ($amount) { return $this->deductBalance($amount); }, 10); return $res; } private function channelLock(float $amount) { $res = $this->locker->channelLock('channelLock', function () use ($amount) { return $this->deductBalance($amount); }); return $res; } }
当我们访问 /balance/consumer?type=noMutex
URL 时,我们可以看到用户的余额变为负数,这显然是不合理的。然而,当我们访问以下 URL 时,我们可以看到用户的余额没有变为负数,这证明了在竞争条件下有效地保护共享资源准确性的有效性。
/balance/consumer?type=flock
: 文件锁/balance/consumer?type=redisLock
: 分布式锁/balance/consumer?type=redLock
: Redlock/balance/consumer?type=channelLock
: 协程级别的互斥锁
注意
关于使用 redisLock
和 redLock
- 使用
redisLock
时,默认使用config/autoload/redis.php
配置文件中的第一个key
配置,这对应于 默认 Redis 实例。您可以选择传递第四个参数string|null $redisPoolName
以根据需要重新指定不同的 Redis 实例。 - 使用
redLock
时,默认使用config/autoload/redis.php
配置文件中的所有key
配置。您可以选择传递第四个参数?array $redisPoolNames = null
以根据需要重新指定不同的 Redis 实例。
文档
您可以在 pudongping/wise-locksmith 找到详细文档。
贡献
可以通过 问题跟踪器提交错误报告(以及小补丁)。对于重大补丁,建议通过分叉存储库并提交拉取请求。
许可
麻省理工学院,见许可文件。