平时加锁总是奔波于命名,害怕命名冲突,不规范等问题,于是使用debug_backtrace
函数通过调用链搞了一搞,通过file,class,function来确定一个锁的位置,从而解决命名冲突,不规范等问题。(迎喷共近)
局部锁
对一段程序进行加锁,保证该局域环境内或跨进程原子性,如发送短信
public function sendSms(string $phone)
{
if (! setnxLock($phone, 10)) return false;
/**
* 处理逻辑
*/
delLock($phone);
return true;
}
全局锁
如一个订单多个地方都在处理,但只能单一处生效
public function handleOrder(string $order_no)
{
if (! setnxLock($order_no, 10, true)) return false;
/**
* 处理逻辑
*/
delLock($order_no, true);
return true;
}
获取缓存锁key
string getLockKey($key, true);
整理汇总
方法放在全局方法中,方便全局调用
<?php
use App\Constants\CacheKey;
if (! function_exists('setnxLock')) {
/**
* 设置缓存锁
*
* @param mixed $key 锁键
* @param int $ttl 过期时间
* @param bool $global 是否全局锁
*
* @return bool
*/
function setnxLock($key, int $ttl = 5, bool $global = false): bool
{
$lock_key = getLockKey($key, $global);
return redis()->setnx($lock_key, 1) && redis()->expire($lock_key, $ttl);
}
}
if (! function_exists('delLock')) {
/**
* 删除缓存锁
*
* @param mixed $key 锁键
* @param bool $global 是否全局锁
*
* @return bool
*/
function delLock($key, bool $global = false): bool
{
$lock_key = getLockKey($key, $global);
return redis()->del($lock_key);
}
}
if (! function_exists('getLockKey')) {
/**
* 获取缓存锁
*
* @param mixed $key 锁键
* @param bool $global 是否全局锁
*
* @return string
*/
function getLockKey($key, bool $global = false): string
{
if ($global) return CacheKey::GLOBAL_LOCK_CACHE_KEY . $key;
return CacheKey::COMMON_LOCK_CACHE_KEY . md5(implode('', lockBacktrace())) . ':' . $key;
}
}
if (! function_exists('lockBacktrace')) {
/**
* 获取调用位置
*
* @return array
*/
function lockBacktrace(): array
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)[4];
$file = $backtrace['file'] ?? '';
$class = $backtrace['class'] ?? '';
$function = $backtrace['function'] ?? '';
return compact('file', 'class', 'function');
}
}
缓存key管理类
class CacheKey extends AbstractConstants
{
// 系统配置缓存
const SYSTEM_CONFIG_CACHE_KEY = 'hash:system_config_cache';
// 全局缓存锁
const GLOBAL_LOCK_CACHE_KEY = 'string:global_lock:';
// 普通缓存锁
const COMMON_LOCK_CACHE_KEY = 'string:common_lock:';
}