ToB企服应用市场:ToB评测及商务社交产业平台

标题: redis分布式锁原子性设置超时问题 [打印本页]

作者: 水军大提督    时间: 2023-2-28 00:23
标题: redis分布式锁原子性设置超时问题
1.问题:

  最近客服有报无法上报运动记录,通过日志查看是分布式锁等待超时所致。
  redis出现一个分布式锁的TTL为-1,正常情况都会设置超时时间的。
  
 
2.分析:

通过k8s发现sport服务在50几天内重启了40几次,机器上内存比较紧缺,暂时只能重启,占用内存高的问题也先不解决。
看下之前加锁的代码:
acquire_lock
  1.  def acquire_lock(redis_client, lock_key, lock_value, expire=10, timeout=5):
  2.     """
  3.     获取锁
  4.     :param redis_client: redis;连接
  5.     :param lock_key: lock key
  6.     :param lock_value: lock value
  7.     :param expire: lock key过期时间
  8.     :param timeout: 等待超时
  9.     :return:
  10.     """
  11.     assert isinstance(redis_client, RedisClient), [type(RedisClient), RedisClient]
  12.     wait_time = 0
  13.     sleep_time = 0.05
  14.     with redis_client.ctx_conn as conn:
  15.         while True:
  16.             stime = time.time()
  17.             if conn.execute('SETNX', lock_key, lock_value):
  18.                 conn.execute('EXPIRE', lock_key, expire)
  19.                 return True
  20.             elif not conn.execute('TTL', lock_key):
  21.                 conn.execute('EXPIRE', lock_key, expire)
  22.             logger.info("acquire_lock :%s" % lock_key)
  23.             gevent.sleep(sleep_time)
  24.             etime = time.time()
  25.             wait_time += etime - stime
  26.             if wait_time >= timeout:
  27.                 break
  28.     assert 0, [conn, lock_key, lock_value, expire, timeout]
复制代码
从现状来分析,应该是SETNX之后没有执行EXPIRE,正常执行肯定没有问题;
我估计是因为进程刚好执行到SETNX的时候被k8s杀了,导致EXPIRE没有去执行
 
3.解决方案:

  研究了一下redis的set命令,果然有这个功能
  SET', lock_key, lock_value, NX, EX, expire
  判断TTL 是-1的时候:
  ttl当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间
  
原子操作set
  1.  def acquire_lock(redis_client, lock_key, lock_value, expire=10, timeout=5):
  2.     """
  3.     获取锁
  4.     :param redis_client: redis;连接
  5.     :param lock_key: lock key
  6.     :param lock_value: lock value
  7.     :param expire: lock key过期时间
  8.     :param timeout: 等待超时
  9.     :return:
  10.     """
  11.     assert isinstance(redis_client, RedisClient), [type(RedisClient), RedisClient]
  12.     wait_time = 0
  13.     sleep_time = 0.05
  14.     logger.info("acquire_lock lock_key:%s lock_value:%s" % (lock_key, lock_value))
  15.     with redis_client.ctx_conn as conn:
  16.         while True:
  17.             stime = time.time()
  18.             if conn.execute('SET', lock_key, lock_value, "NX", "EX", expire):
  19.                 return True
  20.             elif conn.execute('TTL', lock_key) == -1:      
  21.                 conn.execute('EXPIRE', lock_key, expire)
  22.             gevent.sleep(sleep_time)
  23.             etime = time.time()
  24.             wait_time += etime - stime
  25.             if wait_time >= timeout:
  26.                 break
  27.     assert 0, [conn, lock_key, lock_value, expire, timeout]
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4