本文目录导读:

在PHP项目中对接分布式锁组件,通常有以下几种主流方案,我会逐一说明实现方式和适用场景:
基于Redis的分布式锁
使用Predis/PhpRedis + Redlock算法
<?php
use Predis\Client;
class RedisDistributedLock
{
private $redis;
private $lockKey;
private $lockValue;
private $ttl;
public function __construct($redisConfig)
{
$this->redis = new Client($redisConfig);
}
/**
* 获取锁
*/
public function acquire($key, $ttl = 30)
{
$this->lockKey = "lock:{$key}";
$this->lockValue = uniqid('', true);
$this->ttl = $ttl;
// 使用SET NX EX 原子操作
$result = $this->redis->set(
$this->lockKey,
$this->lockValue,
'NX',
'EX',
$ttl
);
return $result !== null;
}
/**
* 释放锁(使用Lua脚本保证原子性)
*/
public function release()
{
$script = '
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
';
return $this->redis->eval(
$script,
1,
$this->lockKey,
$this->lockValue
);
}
/**
* 带重试的锁获取
*/
public function acquireWithRetry($key, $ttl = 30, $retryDelay = 200, $maxRetries = 3)
{
$attempts = 0;
while ($attempts < $maxRetries) {
if ($this->acquire($key, $ttl)) {
return true;
}
$attempts++;
usleep($retryDelay * 1000);
}
return false;
}
}
// 使用示例
$lock = new RedisDistributedLock([
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
]);
if ($lock->acquire('order:123')) {
try {
// 执行临界区代码
echo "处理订单123";
} finally {
$lock->release();
}
}
使用Laravel框架的Redis锁
<?php
use Illuminate\Support\Facades\Redis;
use Illuminate\Cache\RedisLock;
// Laravel 内置锁
$lock = Redis::lock('order:123', 30);
if ($lock->get()) {
try {
// 执行业务逻辑
ProcessOrder::dispatch();
} finally {
$lock->release();
}
}
// 或者简写方式
Redis::lock('order:123', 30)->block(5, function () {
// 自动释放锁
ProcessOrder::dispatch();
});
基于ZooKeeper的分布式锁
<?php
// 需要安装 php-zookeeper 扩展
class ZookeeperDistributedLock
{
private $zookeeper;
private $lockPath;
private $nodePath;
public function __construct($hosts)
{
$this->zookeeper = new Zookeeper($hosts);
}
/**
* 创建锁
*/
public function createLock($lockName)
{
$this->lockPath = "/locks/{$lockName}";
// 创建持久节点作为锁根
if (!$this->zookeeper->exists($this->lockPath)) {
$this->zookeeper->create(
$this->lockPath,
'',
[
[
'perms' => Zookeeper::PERM_ALL,
'scheme' => 'world',
'id' => 'anyone'
]
]
);
}
}
/**
* 获取锁
*/
public function acquire($lockName)
{
$this->createLock($lockName);
// 创建临时顺序节点
$this->nodePath = $this->zookeeper->create(
$this->lockPath . '/lock-',
session_id(),
[
[
'perms' => Zookeeper::PERM_ALL,
'scheme' => 'world',
'id' => 'anyone'
]
],
Zookeeper::EPHEMERAL | Zookeeper::SEQUENCE
);
// 获取所有子节点
$children = $this->zookeeper->getChildren($this->lockPath);
sort($children);
// 判断是否为最小节点
$currentNode = basename($this->nodePath);
return $children[0] === $currentNode;
}
/**
* 释放锁
*/
public function release()
{
if ($this->nodePath && $this->zookeeper->exists($this->nodePath)) {
$this->zookeeper->delete($this->nodePath);
$this->nodePath = null;
}
}
public function __destruct()
{
$this->release();
$this->zookeeper->close();
}
}
使用etcd分布式锁
<?php
// 需要安装 etcd-php 库
use Etcd\Client;
class EtcdDistributedLock
{
private $client;
private $lockKey;
private $leaseId;
public function __construct($endpoints)
{
$this->client = new Client($endpoints);
}
/**
* 获取锁
*/
public function acquire($key, $ttl = 30)
{
$this->lockKey = "locks/{$key}";
// 创建租约
$response = $this->client->leaseGrant(['TTL' => $ttl]);
$this->leaseId = $response['ID'];
// 尝试获取锁(原子操作)
$response = $this->client->txn([
'compare' => [
[
'target' => 'CREATE',
'key' => $this->lockKey,
'result' => 'EQUAL',
'value' => 0
]
],
'success' => [
[
'requestPut' => [
'key' => $this->lockKey,
'value' => uniqid(),
'lease' => $this->leaseId
]
]
],
'failure' => []
]);
if ($response['succeeded']) {
return true;
}
$this->leaseRevoke();
return false;
}
/**
* 释放锁
*/
public function release()
{
if ($this->lockKey) {
$this->client->delete($this->lockKey);
}
$this->leaseRevoke();
}
private function leaseRevoke()
{
if ($this->leaseId) {
$this->client->leaseRevoke(['ID' => $this->leaseId]);
$this->leaseId = null;
}
}
}
使用成熟的分布式锁组件
安装组件(以Laravel为例)
composer require graze/dog-statsd # 或者 composer require php-etl/redis-lock
使用通用分布式锁库
<?php
// 使用 symfony/lock 组件
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore;
use Predis\Client;
class DistributedLockService
{
private $factory;
public function __construct($redisConfig)
{
$redis = new Client($redisConfig);
$store = new RedisStore($redis);
$this->factory = new LockFactory($store);
}
/**
* 执行带锁的任务
*/
public function executeWithLock($lockName, callable $callback, $ttl = 30)
{
$lock = $this->factory->createLock($lockName, $ttl);
if (!$lock->acquire()) {
throw new \RuntimeException("无法获取锁: {$lockName}");
}
try {
return $callback();
} finally {
$lock->release();
}
}
/**
* 等待获取锁
*/
public function executeWithWait($lockName, callable $callback, $ttl = 30, $waitSeconds = 5)
{
$lock = $this->factory->createLock($lockName, $ttl);
$lock->acquire(true); // true表示阻塞等待
try {
return $callback();
} finally {
$lock->release();
}
}
}
最佳实践建议
封装统一的锁服务
<?php
interface DistributedLockInterface
{
public function acquire($key, $ttl = 30);
public function release();
public function acquireWithBlock($key, $ttl = 30, $waitSeconds = 5);
}
class DistributedLockFacade
{
private static $instance;
private $implementations = [];
public static function getInstance($type = 'redis')
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance->getImplementation($type);
}
public function getImplementation($type)
{
if (!isset($this->implementations[$type])) {
$this->implementations[$type] = $this->createImplementation($type);
}
return $this->implementations[$type];
}
private function createImplementation($type)
{
switch ($type) {
case 'redis':
return new RedisDistributedLock(config('database.redis.default'));
case 'zookeeper':
return new ZookeeperDistributedLock(config('zookeeper.hosts'));
case 'etcd':
return new EtcdDistributedLock(config('etcd.endpoints'));
default:
throw new \InvalidArgumentException("不支持的锁类型: {$type}");
}
}
}
// 使用示例
$lock = DistributedLockFacade::getInstance('redis');
if ($lock->acquire('payment:order:123', 30)) {
try {
// 支付处理
$paymentService->process();
} finally {
$lock->release();
}
}
配置示例(.env文件)
LOCK_DRIVER=redis # Redis配置 REDIS_HOST=127.0.0.1 REDIS_PORT=6379 REDIS_PASSWORD= REDIS_DATABASE=0 # ZooKeeper配置 ZOOKEEPER_HOSTS=127.0.0.1:2181,127.0.0.1:2182 # etcd配置 ETCD_ENDPOINTS=http://127.0.0.1:2379,http://127.0.0.1:2380
注意事项:
- 锁超时:必须设置合理的TTL,防止死锁
- 锁释放:确保在finally中释放锁,使用try-finally模式
- 可重入性:如果需要可重入锁,需要额外处理
- 性能考虑:Redis性能最优,ZooKeeper可靠性最高
- 时钟同步:使用Redlock算法时要注意时钟漂移问题
- 监控告警:对锁获取失败等情况进行监控
选择哪种方案取决于你的具体需求:高并发场景推荐Redis,需要强一致性推荐ZooKeeper或etcd,分布式环境复杂推荐使用成熟的第三方组件。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播