class CDisLockObj : public CIMutex
{
public:
CDisLockObj(CRedisDBInterface &cRedisDB, const std::string& strKey)
: m_cRedisDB(cRedisDB), m_strKey(strKey), m_strUUID(COperateUUID::GenerateUUID()) {}; //注意:此处必须生成一个UUID
virtual ~CDisLockObj(){};
virtual bool DoLock(int nTimeOut = nNO_END_LOCKER)
{
if (nNO_BLOCK_LOCKER == nTimeOut)
{
return TryLock();
}
else if (nNO_END_LOCKER >= nTimeOut)
{
return Lock(DEFAULT_MAX_OVERTIME);
}
else
{
return Lock(nTimeOut);
}
}
virtual bool DoUnLock(void)
{
return ReleaseLock();
}
private:
bool Lock(uint64_t nExpire = DEFAULT_OVERTIME, int nSleepTime = DEFAULT_SLEEPTIME)
{
bool bIsLockAvailable = true;
TC_TimeProvider::getInstance()->getNow(&m_tmTimerRecord);
do
{
timeval tmCurrentTime;
TC_TimeProvider::getInstance()->getNow(&tmCurrentTime);
long tmSpacems = (tmCurrentTime.tv_sec - m_tmTimerRecord.tv_sec) * 1000 + (tmCurrentTime.tv_usec - m_tmTimerRecord.tv_usec) / 1000;
if (long(nExpire * 1000) < tmSpacems)
{
bIsLockAvailable = false;
LOG_ERROR("Lock key:" << m_strKey << " overtime, uuid:" << m_strUUID << ", nExpire:" << nExpire);
break;
}
if (EMRStatus::EM_KVDB_SUCCESS == m_cRedisDB.SetEXNX(m_strKey, m_strUUID, nExpire)) //注意:这里的value里面放的是UUID
{
LOG_INFO("CDistributedLock lock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " success");
break;
}
usleep(nSleepTime);
} while (true);
return bIsLockAvailable;
}
bool TryLock(uint64_t nExpire = DEFAULT_OVERTIME)
{
if (EMRStatus::EM_KVDB_SUCCESS == m_cRedisDB.SetEXNX(m_strKey, m_strUUID, nExpire))
{
LOG_INFO("CDistributedLock trylock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " success");
return true;
}
else
{
LOG_WARN("CDistributedLock trylock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " failed");
return false;
}
}
bool ReleaseLock()
{
std::string strValue;
if (EMRStatus::EM_KVDB_SUCCESS != m_cRedisDB.Get(m_strKey, strValue)) //每次解锁前需要检查锁中内容,必须是自己实际加的锁,别人加的锁是不能解的;
{
LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed, can not get value");
return false;
}
if (strValue != m_strUUID)
{
LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed, uuid:" << m_strUUID << " != value:" << strValue);
return false;
}
if (EMRStatus::EM_KVDB_SUCCESS != m_cRedisDB.DelKey(m_strKey))
{
LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed. uuid:" << m_strUUID);
return false;
}
LOG_INFO("CDistributedLock release key:" << m_strKey << " success, uuid:" << m_strUUID);
return true;
}
private:
CRedisDBInterface &m_cRedisDB;
std::string m_strKey;
timeval m_tmTimerRecord; //基点时间,以此为基础进行计时
std::string m_strUUID; //保证解锁、加锁 操作对应的同一把锁,不能是其他人的锁;
};
{
public:
CDisLockObj(CRedisDBInterface &cRedisDB, const std::string& strKey)
: m_cRedisDB(cRedisDB), m_strKey(strKey), m_strUUID(COperateUUID::GenerateUUID()) {}; //注意:此处必须生成一个UUID
virtual ~CDisLockObj(){};
virtual bool DoLock(int nTimeOut = nNO_END_LOCKER)
{
if (nNO_BLOCK_LOCKER == nTimeOut)
{
return TryLock();
}
else if (nNO_END_LOCKER >= nTimeOut)
{
return Lock(DEFAULT_MAX_OVERTIME);
}
else
{
return Lock(nTimeOut);
}
}
virtual bool DoUnLock(void)
{
return ReleaseLock();
}
private:
bool Lock(uint64_t nExpire = DEFAULT_OVERTIME, int nSleepTime = DEFAULT_SLEEPTIME)
{
bool bIsLockAvailable = true;
TC_TimeProvider::getInstance()->getNow(&m_tmTimerRecord);
do
{
timeval tmCurrentTime;
TC_TimeProvider::getInstance()->getNow(&tmCurrentTime);
long tmSpacems = (tmCurrentTime.tv_sec - m_tmTimerRecord.tv_sec) * 1000 + (tmCurrentTime.tv_usec - m_tmTimerRecord.tv_usec) / 1000;
if (long(nExpire * 1000) < tmSpacems)
{
bIsLockAvailable = false;
LOG_ERROR("Lock key:" << m_strKey << " overtime, uuid:" << m_strUUID << ", nExpire:" << nExpire);
break;
}
if (EMRStatus::EM_KVDB_SUCCESS == m_cRedisDB.SetEXNX(m_strKey, m_strUUID, nExpire)) //注意:这里的value里面放的是UUID
{
LOG_INFO("CDistributedLock lock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " success");
break;
}
usleep(nSleepTime);
} while (true);
return bIsLockAvailable;
}
bool TryLock(uint64_t nExpire = DEFAULT_OVERTIME)
{
if (EMRStatus::EM_KVDB_SUCCESS == m_cRedisDB.SetEXNX(m_strKey, m_strUUID, nExpire))
{
LOG_INFO("CDistributedLock trylock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " success");
return true;
}
else
{
LOG_WARN("CDistributedLock trylock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " failed");
return false;
}
}
bool ReleaseLock()
{
std::string strValue;
if (EMRStatus::EM_KVDB_SUCCESS != m_cRedisDB.Get(m_strKey, strValue)) //每次解锁前需要检查锁中内容,必须是自己实际加的锁,别人加的锁是不能解的;
{
LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed, can not get value");
return false;
}
if (strValue != m_strUUID)
{
LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed, uuid:" << m_strUUID << " != value:" << strValue);
return false;
}
if (EMRStatus::EM_KVDB_SUCCESS != m_cRedisDB.DelKey(m_strKey))
{
LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed. uuid:" << m_strUUID);
return false;
}
LOG_INFO("CDistributedLock release key:" << m_strKey << " success, uuid:" << m_strUUID);
return true;
}
private:
CRedisDBInterface &m_cRedisDB;
std::string m_strKey;
timeval m_tmTimerRecord; //基点时间,以此为基础进行计时
std::string m_strUUID; //保证解锁、加锁 操作对应的同一把锁,不能是其他人的锁;
};
#endif
分布式锁设计原理:
加锁:
必须加的是一把可识别出是自己的加的锁;
检查加锁是否成功:
超时,则加锁失败;
解锁:
可以解开自己加的锁;
仅可以解开 自己等的那把锁;
分布式锁遇到的问题:
1、数据库中已存在锁的情况下,如何破锁?
redis:通过设定 key的过期时间,自动破锁;
mysql:通过加锁时做记录,解锁时做判定,解开的是自己等待的那把锁;
2、多线程释放锁的时候,不应释放别人加的锁?
redis:加锁成功后,记录value作为uuid;解锁时判断uuid是否合理,来进行解锁;
mysql:加锁成功后,记录uuid,删除时,where语句中带uuid。