payed_key = VALENTINES_DAY_2019_IS_PAYED_DATE_FORMAT % (uid, t_uid, date_str,event_name)
# 拿到所有的文件,进行扣钱,发送操作
with events_cacher_v2.get_redis_lock(payed_key):
if events_cacher_v2.redis_con.get(payed_key):
raise xxxxxxx('xxxx')
events_cacher_v2.redis_con.setex(payed_key, value='1', time=20)
show_id = show_cacher.get_user_current_show_id(t_uid)
credits_dict = money_cacher.get_user_credits_by_uid(uid)
if credits_dict.get('credits', 0) < VALENTINES_DAY_CREDITS_2019:
raise xxxxxxx('golds not enough')
# 扣钱
try:
money_cacher.A2B_send_credits(uid, t_uid, VALENTINES_DAY_CREDITS_2019, VALENTINES_DAY_GIFT_ID_2019, 1, show_id, send_time=datetime.now())
user_cacher.A2B_send_credits(uid, t_uid, VALENTINES_DAY_CREDITS_2019, VALENTINES_DAY_GIFT_ID_2019, 1, show_id)
except Exception:
raise xxxxxxx('golds not enough')
# 发送语音+文字
imgae_dict = {
"width": 1242,
"height": 932,
"type": 0,
"uri": image_url,
"urls": [
result_image_url
]
}
audio_dict = {
"audio_url": audio_url,
"duration": audio_duration
}
send_money_voice(t_uid, uid, audio_dict, imgae_dict, text=msg)
# 标记redis-key
events_cacher_v2.redis_con.setex(payed_key, value='1', time=DEFAULT_15DAYS_EXPIRE_TIME)
其中:
def get_redis_lock(self, key):
return acquire_redis_lock(
django_settings.noflush_redis_con,
'__%s:lock__' % key,
expire=DEFAULT_REDIS_LOCK_EXPIRE_TIME,
auto_renewal=True)
def acquire_redis_lock(redis_cache, name, expire=None, auto_renewal=False):
from common.redis_cache import RedisCache
if isinstance(redis_cache, RedisCache):
real_redis_con = redis_cache.vip_redis_con
else:
assert isinstance(redis_cache, StrictRedis)
real_redis_con = redis_cache
return redis_lock.Lock(redis_client=real_redis_con, name=name, expire=expire, auto_renewal=auto_renewal)
代码如上,但是上线之后,一直有重复购买,重复扣钱的情况发生,理论上的一天只允许购买一次的效果达不到
想了一下是否有可能
- (1)、客户端1获取锁成功
- (2)、客户端1在某个操作上阻塞了很长时间
- (3)、过期时间到,锁自动释放
- (4)、客户端2获取到了对应同一个资源的锁
- (5)、客户端1从阻塞中恢复过来,认为自己依旧持有锁,继续操作同一个资源,导致互斥性失效
或者(1)、客户端1获取锁成功。
- (2)、客户端1访问共享资源。
- (3)、客户端1为了释放锁,先执行'GET'操作获取随机字符串的值。
- (4)、客户端1判断随机字符串的值,与预期的值相等。
- (5)、客户端1由于某个原因阻塞住了很长时间。
- (6)、过期时间到了,锁自动释放了。
- (7)、客户端2获取到了对应同一个资源的锁。
- (8)、客户端1从阻塞中恢复过来,执行DEL操纵,释放掉了客户端2持有的锁。
2020-3-24回顾
好稚嫩的写法啊,hhh
这地方完全没必要用锁的,利用redis的单进程性质做一个setnx就可以了,失败了就认为已经购买了。或者再加一层sql检查就好了。
2021-3-5
考虑一下是否存在主从延迟,宕机等情况。