先简述碰到的一个问题,管理后台,进行数据保存的时候,一秒钟,多次的提交了同一信息。先不评论前端的问题,光就后端接口这里,说一下处理思路。
后端要防止这个问题出现,因为后端用的是python的django框架,所以首先想到的是限流的插件django-ratelimit。这个是django官方提供的
@ratelimit(key='ip', rate='1/30s', block=True)
装饰器就可以了,网站上的资料不多,大部分都是英文的。在这里稍微解释下参数:
key='ip', 必填项,标识按照IP划分,我理解的是同一IP,遵循后面参数设定的规则。
rate='1/30s',必填项,设置的频率值,这个意思是30秒内执行一次,也可以按照 “分”,“时”,“日” 等划分,很灵活的配置,比如每分钟执行5次,可以这样写rate='5/m',这里参考文档足够弄明白
block=True,在这里吃了个亏,默认是False,加上了装饰器没写该参数,访问不受限制,没有达到间隔时间内不能再访问的预期效果,果断回去翻文档,
但是这个不满足我们的使用场景,因为我们是uwsgi+nginx的部署方式,uwsgi开了多个进程。这个ratelimit是基于同一会话来限制的,万一下次请求,uwsgi分配给别的进程,他多个进程间内存不共享,就会出现限制不住的情况
第二个想到的思路是用redis锁。redis是比较好弄也比较保险的。并且项目之前就用到redis弄缓存了,所以开发起来也是非常方便的。
假如不用python基于redis锁的一些库,自己写也是非常好弄的,直接装个redis的库,然后填好配置
pool_online = redis.ConnectionPool(host='host', port=port, db=0, password='password', decode_responses=True)
rcon_online = redis.Redis(connection_pool=pool_online)
注意上边加了一个decode_responses=True,这个是让插入的字符串不要乱码的
然后视图函数里,一进来就先查询key存不存在,存在直接抛错,不存在进新建key,整个逻辑处理完后再把可以删除掉就可。
key_now = "online:key:set_api_{}".format(id)
try:
if rcon.getkey_now:
return _JsonResponse({}, ERROR, '当前任务正在执行,请勿重复点击')
else:
rcon.set(key_now id) # 创建key
rcon.expire(key_now 60) # 假如程序结尾没有主动释放,那就1分钟后,自动释放
except Exception:
pass
rcon.delete(key_now )# 主动释放redis锁
redis锁说到底其实就是玩了一个redis的key的过期时间
第三个想到的是,从数据层面做一下限制,比如加锁,让请求就算同时进来了,也更新不成功。但是枷锁有个限制,如果这个提交是更新一条记录,那加锁就没意义,反正多少个请求进来,字段都是一模一样,只是多更新了几次而已。但要是加插入意向锁,就会让你这几次提交都成功。所以还得配合着关键字段的唯一索引来保证。总之结合自己业务具体分析吧。