fiberphp / lock
🔒 FiberPHP 分布式锁组件 —— 基于 Redis 的分布式锁实现,支持自动续期、阻塞等待、可重入,协程安全,开箱即用。
Requires
- php: >=8.3
- fiberphp/config: dev-master
- fiberphp/container: dev-master
- fiberphp/contract: dev-master
- fiberphp/discovery: dev-master
- fiberphp/redis: dev-master
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
- workerman/workerman: Lock 看门狗自动续期依赖 Workerman\Timer,纯 CLI 场景可不安
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-09 05:55:01 UTC
README
基于 Redis 的分布式锁组件,为常驻内存的 Workerman/FiberPHP 应用提供原子加锁、看门狗自动续期、安全释放(token 校验防误删)、阻塞获取与闭包式执行等能力。
特性
- 原子加锁:使用
SET key token NX EX ttl单命令原子获取,避免「检查 + 设置」竞争。 - 安全释放:释放锁通过 Lua 脚本比对 token,仅删除自己的锁,避免跨进程误删。
- 看门狗续期:基于
Workerman\Timer按配置间隔自动续期,业务执行超时不会导致锁提前过期被他人抢占。 - 多种获取方式:非阻塞
acquire、阻塞block(轮询等待)、闭包run(自动 try-finally 释放)。 - 驱动可扩展:内置 RedisLockDriver,可通过
Locker::extend()注册自定义驱动(数据库锁、文件锁等)。 - Redis 连接可配:支持使用任意
config/redis.php中定义的连接。 - 懒初始化:首次调用自动初始化,无需 ServiceProvider。
安装
composer require fiberphp/lock
包内置 config/lock.php 默认配置由 config 包自动合并,装包即用;如需调整,在应用 config/lock.php 放置同名键覆盖默认值。
配置
配置文件位于 config/lock.php,通过 config('lock.xxx') 访问:
<?php
return [
'enable' => true,
// Redis 连接名,对应 config/redis.php 中的键
'connection' => 'default',
// 锁 key 前缀
'prefix' => 'lock:',
// 默认锁超时(秒),到期后自动释放,防死锁
'default_ttl' => 30,
// 看门狗续期间隔(秒),0 = 不启用自动续期;必须 < default_ttl
'watchdog_interval' => 10,
// 阻塞获取锁时的轮询间隔(毫秒)
'block_wait_ms' => 100,
// 阻塞获取锁的最大等待时间(秒)
'block_timeout' => 5,
];
关键提醒:
watchdog_interval必须小于锁的 TTL(default_ttl或acquire($ttl)传入的$ttl>),否则续期逻辑不会启动,避免续期晚于过期导致的乱序。
使用
方式一:非阻塞获取(推荐高并发场景快速失败)
use FiberPHP\Lock\Locker;
$lock = Locker::acquire('order:' . $orderId);
if ($lock === null) {
throw new \RuntimeException('操作太频繁,请稍后重试');
}
try {
// 订单业务
} finally {
$lock->release();
}
自定义 TTL 和 token:
$lock = Locker::acquire('stock:sku_999', ttl: 15, token: 'my-unique-token');
方式二:阻塞获取(业务等待)
$lock = Locker::block('payment:order_123', ttl: 30, timeout: 5);
if ($lock === null) {
throw new \RuntimeException('系统繁忙,超过 5 秒仍未获取到锁');
}
try {
// 支付处理...
} finally {
$lock->release();
}
方式三:闭包式执行(最推荐)
自动完成「获取 → 执行 → 释放」,失败抛 LockException。
use FiberPHP\Lock\Locker;
$result = Locker::run('deduct_stock:' . $skuId, function () use ($skuId, $num) {
return OrderService::deductStock($skuId, $num);
}, ttl: 20, timeout: 3);
手动续期
$lock = Locker::acquire('longtask', ttl: 10);
// 一段长逻辑...
$lock->renew(); // 手动把锁续期到 10 秒(配置 TTL)
token 检查
$lock = Locker::acquire('demo');
echo $lock->getKey(); // "lock:demo"
echo $lock->getToken(); // 32 位随机十六进制 token
核心设计
防误删:token + Lua
每把锁 SET 的 value 是全局唯一的随机 token。release() / renew() 都通过 Lua 脚本原子执行「GET 比对 → DEL /
EXPIRE」,保证过期后被其他进程拿到的锁,不会被旧持有者错误释放或续期。
-- 释放锁
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
end
return 0
看门狗自动续期
锁创建时,如果 watchdog_interval > 0 且 < TTL,会注册 Workerman\Timer 按间隔对锁调用 EXPIRE。若续期时发现 token
不再匹配(自己的锁已过期被替换),会自动清理定时器,避免无用轮询。
⚠️ 看门狗在 Worker 进程中有效。在 Worker 启动外使用(如 CLI 脚本),看门狗会因
Timer::add()无效而静默不启动,此时业务必须靠自己估算好 TTL。
驱动可扩展
内置 RedisLockDriver(SET NX EX + Lua 校验)。自定义驱动实现 LockDriverInterface 后通过 Locker::extend() 注册:
use FiberPHP\Lock\Locker;
Locker::extend('file', fn () => new FileLockDriver());
// config/lock.php: 'driver' => 'file'
典型场景
| 场景 | 建议 |
|---|---|
| 秒杀扣库存 | Locker::acquire 非阻塞快速失败,返回"手慢了" |
| 下单幂等(按订单号) | Locker::run 闭包,简单可靠 |
| 定时任务单实例执行 | Locker::acquire('cron:xxx', 55, 'worker-' . posix_getpid()),TTL 略小于任务周期 |
| 资源同步(上传大文件/写大文件) | Locker::block,等待时间可放宽 |
目录结构
src/
├── Contract/
│ └── LockDriverInterface.php # 锁驱动契约
├── Driver/
│ └── RedisLockDriver.php # Redis 锁驱动(SET NX EX + Lua)
├── Install.php # 安装钩子,定义配置路径
├── Lock.php # 锁句柄(token/释放/续期/看门狗)
├── Locker.php # 静态门面(对外入口)
├── LockException.php # 锁异常
└── LockProvider.php # 服务提供者
依赖说明
| 依赖 | 说明 |
|---|---|
| PHP >= 8.3 | 运行环境要求 |
fiberphp/redis | 提供 Redis 客户端,需配置名为 default 的连接(可配) |
fiberphp/contract | ConfigRepository / ProviderInterface 契约 |
fiberphp/config | 配置读取 |
fiberphp/container | Locker::init() 通过容器解析 ConfigRepository |
fiberphp/discovery | PackageInstaller 配置文件发布 |
workerman/workerman | (suggest)看门狗自动续期依赖 Workerman\Timer,纯 CLI 场景可不安 |
命名空间为 FiberPHP\Lock\(PSR-4,对应 src/ 目录)。
License
MIT License (c) 2026 庞斌,详见 LICENSE。