简言
1. redis实现分布式锁的原理,这里不再赘述,不清楚的可以参见笔者的这篇博客
https://blog.csdn.net/yzf279533105/article/details/100524700
2. 解锁时使用lua脚本,由于hiredis是根据空格来解析cmd参数的,但是lua中肯定有空格,所以解锁的redis命令要分开格式化
3. 重点关注 Unlock()函数即可,关注里面如何调用lua脚本的
代码如下:
头文件(仅贴出主要代码)
// redis锁
struct RedisLock
{
string key; // 锁的key
int randval; // 随机值
};
// 加锁(锁数据,过期时间,单位:秒)
bool Lock(RedisLock lockkey, uint32_t expire);
// 解锁
bool Unlock(RedisLock lockkey);
cpp文件
// 加锁(锁数据,过期时间,单位:秒)
bool CRedisClient::Lock(RedisLock lockkey, uint32_t expire)
{
bool bSuc = connect();
if (!bSuc)
{
ERROR("CRedisClient::Lock(), connect failed");
return false;
}
ostringstream os;
os<< "set " << lockkey.key <<" "<< lockkey.randval << " ex " << expire << " nx";
// set命令
CAutoRedisReply autoR;
redisReply* r = (redisReply*)redisCommand(m_redisCtx, os.str().c_str());
if (NULL == r)
{
ERROR("CRedisClient::Lock(),call redisCommand() error,command=%s, redis break connection,m_redisCtx: %p",os.str().c_str(), m_redisCtx);
m_bConnected = false;
return false;
}
autoR.set(r);
if (r->type!=REDIS_REPLY_STATUS || r->str==NULL || strcasecmp(r->str, "OK") != 0)
{
ERROR("CRedisClient::Lock(),result error, type=%d, command=%s, errmsg=%s", r->type, os.str().c_str(), r->str);
return false;
}
return true;
}
// 解锁
bool CRedisClient::Unlock(RedisLock lockkey)
{
// 注意:由于hiredis是根据空格来解析cmd参数的,但是lua中肯定有空格,所以这里的命令要分开格式化;不要像上面的那样直接用ostringstream来合成所有的字符串
char script[256] = {0};
sprintf(script, "if redis.call('get', KEYS[1]) == '%d' then return redis.call('del', KEYS[1]) else return 0 end", lockkey.randval);
CAutoRedisReply autoR;
// 注意命令格式,不要把参数key格式化到script中,那样会报参数个数不够的错误
redisReply* r = (redisReply*)redisCommand(m_redisCtx, "eval %s 1 %s", script, lockkey.key.c_str());
if (NULL == r)
{
ERROR("CRedisClient::Lock(),call redisCommand() error,command=%s, redis break connection,m_redisCtx: %p",script, m_redisCtx);
m_bConnected = false;
return false;
}
autoR.set(r);
if (r->type!=REDIS_REPLY_INTEGER || r->integer!= 1 || r->str != NULL)
{
ERROR("CRedisClient::Lock(),result error, type=%d, command=%s, r->interger=%d, errmsg=%s", r->type, script, r->integer, r->str);
return false;
}
return true;
}