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都 ...
随机推荐
- 关于Java中对象的向上转型和向下转型
什么是多态? 同一个类调用同一个方法会产生不同的影响/结果 这就是多态 public class Pet{ public void eat(){ System.out.println("Pe ...
- pandlepanlde-01-必备数学知识
文章目录 必备数学知识 数学基础知识 高等数学 线性代数 行列式 矩阵 向量 线性方程组 矩阵的特征值和特征向量 二次型 概率论和数理统计 随机事件和概率 随机变量及其概率分布 多维随机变量及其分布 ...
- Docker私有仓库harbor
Docker私有仓库harbor 目录 Docker私有仓库harbor Harbor私有仓库介绍 Harbor部署 harbor页面不显示排错思路 Harbor的使用 Harbor拉镜像 自制镜像推 ...
- 从浏览器输入域名开始分析DNS解析过程
摘要:DNS(Domain Name System)是域名系统的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,用于 TCP/IP 网络. 本文分享自华为云社区<DNS那些事--从浏 ...
- 日增数据超10PB!揭秘沃尔玛Lakehouse架构选型之路
沃尔玛系统产生了世界上最大和最多样化的数据集之一,每天数据增长超 10 PB. 来自许多不同的来源及其支持的后端系统,一系列大量的业务事件流被发送到主要由 Apache Kafka 支持的消息传递层. ...
- C++中的字符串编码处理
今天由于在项目中用到一些与C++混合开发的东西 ,需要通过socket与C++那边交换数据,没啥特别的,字节码而已,两边确定一种编码规则就行了.我们确定的UTF-8.关于C++的 这种又是宽字节 又是 ...
- laravel ServiceProvider 服务提供者使用案例
1. 实例化一个类 2.全局注册这个类 3.在控制器中使用 public function register() { $this->app->singleton('wxminapp', f ...
- Mac常用文件解压命令
tar 解压:tar xvf fileName.tar 压缩:tar cvf fileName.tar directoryName rar 1.安装rar 下载RAR https://www.rarl ...
- GaussDB(DWS)迁移实践丨row_number输出结果不一致
摘要:迁移前后结果集row_number字段值前后不一致,前在DWS上运行不一致. 本文分享自华为云社区<GaussDB(DWS)迁移 - oracle兼容 --row_number输出结果不一 ...
- Mysql DDL执行方式-pt-osc介绍 | 京东云技术团队
1 引言 大家好,接着上次和大家一起学习了<MySQL DDL执行方式-Online DDL介绍>,那么今天接着和大家一起学习另一种MySQL DDL执行方式之pt-soc. 在MySQL ...