Redis实现访问流量控制
2017-09-03 18:10
1441 查看
为什么需要访问流量控制
对于成熟的系统,一般会提供一些自我保护能力。其中比较重要的就是流量控制能力,一旦大促期间出现突发的流量高峰,系统顶不住的时候,如果没有类似的保护系统,整个系统就可能挂掉。因为在运行的系统有缓存,挂掉的系统可能一时间缓存跟不上,或者刚启动起来的时候还没有缓存(恢复系统过程中,已有缓存已经过期失效),那么很可能系统起来就会挂掉,从而要经过很长时间才能恢复使用,这个恢复过程消耗一个小时、两个小时可能都是比较好的。
一般,在突发高流量场景下,无论如何都不可能短时间的获得特别高的服务能力。所以,不论是系统将近崩溃之前,还是系统崩溃之后需要重新启动的时候,都需要一个流量控制系统来让系统能够服务,或者能够恢复服务。原则是,“只要系统不死,就有东山再起的希望”。
首先,需要说明对于流量控制可以是在接入层入做,比如硬件的F5、软件的Nginx、Apache Httpd。
这里讲述,如果使用redis做访问流量控制,可以怎么做。这里所说的流量控制是限制一定时间内的请求次数,可以是限制IP级别,也可以是限制用户Id级别。
第一种思路
借助redis的key过期机制,生成有效期1分钟的key。当key存在时,计数加1,当key不存在时,创建key,同时设置过期时间。代码如下:
String key = getUserId(); Long value = redisTemplate.opsForValue().get("key");//1 if(value != null) { //2 Long newValue = redisTemplate.opsForValue().increment("key", 1L);//3redisTemplate.opsForValue().increment("key", 1L);//3 if(newValue > MAX_REQUEST) { return false; } }else {//4 redisTemplate.opsForValue().set("key", 1L, 60L, TimeUnit.SECONDS);//5 } return true;
上述代码存在一个问题,就是当运行到2位置时发现value != null,但是当执行3位置前,redis服务器将key对应的值设置为过期了,那么当执行3位置代码时会创建重新生成一个key。但是这个key是没有过期时间的,从而造成数值不断累加,造成之后de的请求全部被拒绝。
第一种思路的修正
很明显,第一种思路由于并发缘故,存在一个漏洞。我们第一想法就是补漏,通过增加过期时间来防止永不过期的问题出现,如下:String key = getUserId(); Long value = redisTemplate.opsForValue().get(key);//1 if(value != null) { //2 Long newValue = redisTemplate.opsForValue().increment(key, 1L);//3redisTemplate.opsForValue().increment("key", 1L);//3 redisTemplate.expire(key, 60L, TimeUnit.SECONDS); if(newValue > MAX_REQUEST) { return false; } }else {//4 redisTemplate.opsForValue().set(key, 1L, 60L, TimeUnit.SECONDS);//5 } return true;
这里一次计算进行了2次或者3次redis请求,感觉有一些浪费,再看一种相对交互较少的思路。
进一步简化
因为redis得incrBy内部对key的存在性进行了判断,我们可以省去判断,即:String key = getUserId(); Long value = redisTemplate.opsForValue().increment(key, 1L);//1 redisTemplate.expire(key, 60L, TimeUnit.SECONDS);//2 if(value > MAX_REQUEST) { return false; } return true;
第二种思路
我们不适用获取然后比较的方式,从key的创建进行优化设计。key的末尾增加时间戳,也就是key-MMddHHmm的形式,那么由于每分钟生成的key是不一样的,也就是自动起到了过期的作用。为了防止无效的key在redis内存中的积压,可以使用一个定时作业进行清理。从而代码会被简化为:
String key = getUserId()+getDate(); Long value = redisTemplate.opsForValue().increment(key, 1L);//1 if(value > MAX_REQUEST) { return false; } return true;
但是这种方式,有一个问题是怎么找到过期的key,如果用keys查找的话会会有问题。一个是消耗时间可能较多,一个是redis集群化后可能不支持keys操作。
相关文章推荐
- Redis实现Restful的访问权限控制(一)
- Redis实现Restful的访问权限控制(二)
- php 基于redis使用令牌桶算法实现流量控制
- Redis实现Restful的访问权限控制(四)
- Redis实现Restful的访问权限控制(三)
- 基于java过滤器实现web系统的IP访问控制
- SpringMVC Mybatis Shiro RestTemplate的实现客户端无状态验证及访问控制【转】
- 内网服务应用—无需端口映射实现从外网访问控制内网电脑
- HttpModule实现系统IP访问控制
- 基于java过滤器实现web系统的IP访问控制
- AngularJs基于角色的前端访问控制的实现
- Linux网络编程面试--流量控制和拥塞控制的实现机制
- AngularJS – 实现基于角色访问控制的 GUI
- 实现postfix基于客户端的访问控制
- EasyDarwin开源摄像机访问EasyCamera中海康摄像头语音对讲和云台控制转发实现
- Nginx、Springmvc实现下载文件访问控制
- 实现单向访问控制 推荐
- mac配置本地环境,用brew下载redis及apache,实现手机访问本地网页
- Flask实现基于角色的访问控制(RBAC)
- yii 权限分级式访问控制的实现(非RBAC法)