Redis解决网络抖动问题
Redis解决网络抖动问题
所谓网络抖动问题, 简单来说就是防止用户短暂的时间内对同一个接口多次点击访问
这里利用的是redis锁的原子性和with Statement上下文管理器实现, 另外该类还支持协程, 可使用async with 调用
1. 源码
FuncDefine.py
def clear_all_lock(PREFIX='lock'):
keys = redis_operator.get_redis_keys_pattern(PREFIX + '*')
for key in keys:
if isinstance(key, bytes):
kwl_py_write_log(key.decode(encoding='utf-8'), msgid='del_redis_key')
redis_operator.delete_redis_key(key.decode(encoding='utf-8'), 1)
def unlock(key):
redis_operator.delete_redis_key(key, 1)
class RedisLock:
DEFAULT_VALUE = 1
PREFIX = 'lock'
def __init__(self, key, lock_time=300):
"""
初始化redis锁
:param key: 关键字
:param lock_time: 上锁时间 5min
"""
self._key = RedisLock.PREFIX + key
self.lock_time = lock_time
self.hold_lock = False
@property
def key(self):
return self._key
@key.setter
def key(self, key):
self._key = RedisLock.PREFIX + key
def __enter__(self):
self.hold_lock = self.acquire()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.hold_lock:
self.release()
return False
async def __aenter__(self):
self.hold_lock = await self.acquire_cas_lock()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.hold_lock:
self.release()
return False
async def acquire_cas_lock(self, lock_time=60):
try:
return await asyncio.wait_for(self.acquire_lock_until_succ(), lock_time)
except asyncio.TimeoutError as e:
return False
async def acquire_lock_until_succ(self):
while redis_operator.set_redis_key_ex_nx(self.key, self.DEFAULT_VALUE, self.lock_time) is not True:
# redis return is None or other
await asyncio.sleep(0.01)
return True
def acquire(self):
"""
设置redis锁
:param key:
:param lock_time:
:return:
"""
try:
return redis_operator.set_redis_key_ex_nx(self.key, self.DEFAULT_VALUE, self.lock_time) is True
except Exception:
return False
def release(self):
redis_operator.delete_redis_key(self.key, 1)
redis_operator.py
import redis
from redisConfig import *
# ------------------------------------------------------------------------------------------------------
# 主从redis,个数一一对应
g_main_redis_pool_list = []
g_slave_redis_pool_list = []
g_main_redis_is_ok = [] # 主redis是否可用True为主ok
g_slave_redis_is_ok = [] # 从redis是否可用
for each_redis in g_main_redis_server:
redis_pool = redis.ConnectionPool(host=each_redis[0], port=each_redis[1], password=each_redis[2], socket_timeout=8,
socket_connect_timeout=5)
g_main_redis_pool_list.append(redis_pool)
g_main_redis_is_ok.append(True)
for each_redis in g_slave_redis_server:
redis_pool = redis.ConnectionPool(host=each_redis[0], port=each_redis[1], password=each_redis[2], socket_timeout=8,
socket_connect_timeout=5)
g_slave_redis_pool_list.append(redis_pool)
g_slave_redis_is_ok.append(True)
def get_redis_by_key(strkey, nums):
return (ord(strkey[0]) + ord(strkey[-1])) % nums
# 从redis取
def get_redis_key(key):
# 根据key来分库
index = get_redis_by_key(key, len(g_main_redis_pool_list))
if g_main_redis_is_ok[index]:
# 主ok
try:
return redis.Redis(connection_pool=g_main_redis_pool_list[index]).get(key)
except Exception:
# 主标记为挂
g_main_redis_is_ok[index] = False
# 主挂了试试从能不能用
g_slave_redis_is_ok[index] = True
if g_slave_redis_is_ok[index]:
# 从ok
try:
return redis.Redis(connection_pool=g_slave_redis_pool_list[index]).get(key)
except Exception as e:
# 从标记为挂
g_slave_redis_is_ok[index] = False
# 从也挂了下回只能尝试使用主
g_main_redis_is_ok[index] = True
# 抛出异常
raise Exception(repr(e))
# 按理不可能出现这种情况,主从皆False,全挂的情况也会至少打开一个
g_main_redis_is_ok[index] = Trueget_redis_by_key
raise Exception('内部错误,get_redis_key运行异常')
# redis存值且设置生命周期
def set_redis_key_ex(key, value, expire):
# 根据key来分库
index = get_redis_by_key(key, len(g_main_redis_pool_list))
if g_main_redis_is_ok[index]:
# 主ok
try:
if expire == 0:
return redis.Redis(connection_pool=g_main_redis_pool_list[index]).set(key, value)
return redis.Redis(connection_pool=g_main_redis_pool_list[index]).setex(key, value, expire)
except Exception:
# 主标记为挂
g_main_redis_is_ok[index] = False
# 主挂了试试从能不能用
g_slave_redis_is_ok[index] = True
if g_slave_redis_is_ok[index]:
# 从ok
try:
if expire == 0:
return redis.Redis(connection_pool=g_slave_redis_pool_list[index]).set(key, value)
return redis.Redis(connection_pool=g_slave_redis_pool_list[index]).setex(key, value, expire)
except Exception as e:
# 从标记为挂
g_slave_redis_is_ok[index] = False
# 从也挂了下回只能尝试使用主
g_main_redis_is_ok[index] = True
# 抛出异常
raise Exception(repr(e))
# 按理不可能出现这种情况,主从皆False,全挂的情况也会至少打开一个
g_main_redis_is_ok[index] = True
raise Exception('内部错误,set_redis_key_ex运行异常')
# redis存值且设置生命周期
def expire_redis_key(key, expire):
# 根据key来分库
index = get_redis_by_key(key, len(g_main_redis_pool_list))
if g_main_redis_is_ok[index]:
# 主ok
try:
if expire == 0:
return 0
return redis.Redis(connection_pool=g_main_redis_pool_list[index]).expire(key, expire)
except Exception:
# 主标记为挂
g_main_redis_is_ok[index] = False
# 主挂了试试从能不能用
g_slave_redis_is_ok[index] = True
if g_slave_redis_is_ok[index]:
# 从ok
try:
if expire == 0:
return 0
return redis.Redis(connection_pool=g_slave_redis_pool_list[index]).expire(key, expire)
except Exception as e:
# 从标记为挂
g_slave_redis_is_ok[index] = False
# 从也挂了下回只能尝试使用主
g_main_redis_is_ok[index] = True
# 抛出异常
raise Exception(repr(e))
# 按理不可能出现这种情况,主从皆False,全挂的情况也会至少打开一个
g_main_redis_is_ok[index] = True
raise Exception('内部错误,expire_redis_key运行异常')
# redis删除key
def delete_redis_key(key, expire):
# 根据key来分库
index = get_redis_by_key(key, len(g_main_redis_pool_list))
if g_main_redis_is_ok[index]:
# 主ok
try:
if expire == 0:
return 0
return redis.Redis(connection_pool=g_main_redis_pool_list[index]).delete(key)
except Exception:
# 主标记为挂
g_main_redis_is_ok[index] = False
# 主挂了试试从能不能用
g_slave_redis_is_ok[index] = True
if g_slave_redis_is_ok[index]:
# 从ok
try:
if expire == 0:
return 0
return redis.Redis(connection_pool=g_slave_redis_pool_list[index]).delete(key)
except Exception as e:
# 从标记为挂
g_slave_redis_is_ok[index] = False
# 从也挂了下回只能尝试使用主
g_main_redis_is_ok[index] = True
# 抛出异常
raise Exception(repr(e))
# 按理不可能出现这种情况,主从皆False,全挂的情况也会至少打开一个
g_main_redis_is_ok[index] = True
raise Exception('内部错误,delete_redis_key运行异常')
def set_redis_key_ex_nx(key, value, expire):
"""如果有键值则不设置"""
# 根据key来分库
index = get_redis_by_key(key, len(g_main_redis_pool_list))
if g_main_redis_is_ok[index]:
# 主ok
try:
if expire == 0:
return 0
return redis.Redis(connection_pool=g_main_redis_pool_list[index]).set(key, value, ex=expire, nx=True)
except Exception:
# 主标记为挂
g_main_redis_is_ok[index] = False
# 主挂了试试从能不能用
g_slave_redis_is_ok[index] = True
if g_slave_redis_is_ok[index]:
# 从ok
try:
if expire == 0:
return 0
return redis.Redis(connection_pool=g_slave_redis_pool_list[index]).set(key, value, ex=expire, nx=True)
except Exception as e:
# 从标记为挂
g_slave_redis_is_ok[index] = False
# 从也挂了下回只能尝试使用主
g_main_redis_is_ok[index] = True
# 抛出异常
raise Exception(repr(e))
# 按理不可能出现这种情况,主从皆False,全挂的情况也会至少打开一个
g_main_redis_is_ok[index] = True
raise Exception('内部错误,set_redis_key_nx_运行异常')
def get_redis_keys_pattern(key_pattern):
from builtins import enumerate
key_set = set()
# 主库找
for index, is_ok in enumerate(g_main_redis_is_ok):
if is_ok:
key_set.update(redis.Redis(connection_pool=g_main_redis_pool_list[index]).keys(key_pattern))
# 从库找
for index, is_ok in enumerate(g_slave_redis_is_ok):
if is_ok:
key_set.update(redis.Redis(connection_pool=g_slave_redis_pool_list[index]).keys(key_pattern))
return key_set
if __name__ == "__main__":
# set_redis_key_ex('ab','a',10)
print(get_redis_key('ab').decode())
2. 使用方法
import FuncDefine
with FuncDefine.RedisLock(rediskey) as lock:
if not lock.hold_lock:
return response(3, '商品添加中,请稍后~', '', [])
3. 源码分析
整体来看也就是接口访问过来的时候, 设置一个redis_key(nx=True, ex=300), 这样在五分钟之内就可以避免重复点击的情况
- 初始化redis, 上下文管理器会触发
__enter__()方法, 从而调用self.acquire()

- 设置redis的键, 如果不加nx=True, redis的set会直接覆盖之前key的值, 这里还存在一个主从redis, 感兴趣可以看看源码

- 当执行完with中的代码块, 会触发
__exit__()函数, 调用函数删除当前redis的key对应的值

- 剩下的一些函数都是封装的一些通用方法, 比如查看当前key值
Redis解决网络抖动问题的更多相关文章
- 豌豆夹Redis解决方式Codis源代码剖析:Proxy代理
豌豆夹Redis解决方式Codis源代码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描写叙述: Codis is a proxy b ...
- SQLServer 2012之AlwaysOn —— 指定数据同步链路,消除网络抖动导致的提交延迟问题
事件起因:近期有研发反应,某数据库从08切换到12环境后,不定期出现写操作提交延迟的问题: 事件分析:在排除了系统资源争用等问题后,初步分析可能由于网络抖动导致同步模式alwayson节点经常出现会话 ...
- centos6.7用yum安装redis解决办法及IP限制配置
在centos6.7用yum安装redis解决办法 - bluesky1 - 博客园 http://www.cnblogs.com/lanblogs/p/6104834.html yum instal ...
- Android App卡顿慢优化之解决内存抖动及内存泄漏
前面一篇博客说到了,内存抖动的第二种情况,就是必须在短时间内创建对象,但是要控制数量:这个问题目前可以使用对象池的方法解决. 3)Object Pools 在程序里面经常会遇到的一个问题是短时间内创建 ...
- Linux 将进程放入后台执行,解决网络,ssh断开导致进程结束(nohup, setsid, &, disown)
Linux 将进程放入后台执行,解决网络,ssh断开导致进程结束(nohup, setsid, &, disown) 1.nohup 命令 我们知道,当用户注销(logout)或者网络断开 ...
- 使用Spring Session和Redis解决分布式Session跨域共享问题
http://blog.csdn.net/xlgen157387/article/details/57406162 使用Spring Session和Redis解决分布式Session跨域共享问题
- 170222、使用Spring Session和Redis解决分布式Session跨域共享问题
使用Spring Session和Redis解决分布式Session跨域共享问题 原创 2017-02-27 徐刘根 Java后端技术 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用 ...
- 为npm设置代理,解决网络问题
为npm设置代理,解决网络问题 npm config set proxy=http://127.0.0.1:1080
- 如何运用PHP+REDIS解决负载均衡后的session共享问题
一.为什么要使用Session共享? 稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名.密码在整个网站的 ...
- ping、网络抖动与丢包
基本概念: ping: PING指一个数据包从用户的设备发送到测速点,然后再立即从测速点返回用户设备的来回时间.也就是俗称的“网络延迟” 一般以毫秒(ms)计算 一般PING在0~100ms都 ...
随机推荐
- 10分钟带你徒手做个Java线程池
摘要:花10分钟开发一个极简版的Java线程池,让小伙伴们更好的理解线程池的核心原理. 本文分享自华为云社区<放大招了,冰河带你10分钟手撸Java线程池,yyds,赶快收藏吧>,作者:冰 ...
- 深度学习04-(Tensorflow简介、图与会话、张量基本操作、Tensorboard可视化、综合案例:线性回归)
深度学习04-Tensorflow 深度学习04-(Tensorflow) Tensorflow概述 Tensorflow简介 什么是Tensorflow Tensorflow的特点 Tensorfl ...
- Java 20 新功能介绍
➜ bin pwd /Users/darcy/develop/jdk-20.0.1.jdk/Contents/Home/bin ➜ bin ./java -version openjdk versio ...
- 用go设计开发一个自己的轻量级登录库/框架吧
用go设计开发一个自己的轻量级登录库/框架吧 几乎每个项目都会有登录,退出等用户功能,而登录又不单仅仅是登录,我们要考虑很多东西. token该怎么生成?生成什么样的? 是在Cookie存token还 ...
- AI天后,在线飙歌,人工智能AI孙燕姿模型应用实践,复刻《遥远的歌》,原唱晴子(Python3.10)
忽如一夜春风来,亚洲天后孙燕姿独特而柔美的音色再度响彻华语乐坛,只不过这一次,不是因为她出了新专辑,而是人工智能AI技术对于孙燕姿音色的完美复刻,以大江灌浪之势对华语歌坛诸多经典作品进行了翻唱,还原度 ...
- #Powerbi 理解VAR函数
VAR意思即为变量,在编程语言中,变量是一个重要概念,DAX作为一种语言也有变量概念,利用VAR,我们可以缩短我们一些DAX语句的长度,更清晰的表达我们的度量值计算逻辑. 举例说明: 我们要计算一个产 ...
- docker(一):Develop faster. Run anywhere.
前言 在进行微服务部署时,首先需要进行部署环境的搭建.目前,Docker 已经成为了微服务部署的主流解决方案之一.Docker 可以帮助我们更快地打包.测试以及部署应用程序,从而缩短从编写到部署运行代 ...
- reverse逆转,即反向排序
reverse逆转,即反向排序 print(Student.objects.all().exclude(nickname='A').reverse()
- uni-app 打包发行
1.云端 发行-原生App-云打包 2.离线 运行-原生App本地打包-生成本地打包资源,如果提示安装依赖包,安装即可 注意:项目的AppID不能为空,请在该项目下的manifest.json中重新获 ...
- Python对两个Excel操作
简介 现在有个需求,我们根据需要 data.xlsx 中某些单元格的内容来查找 find.xlsx 中的某些內容. 数据内容(为了数据安全,所有数据均已模糊处理) data.xlsx内容: find. ...