我们的web服务可能由于过高的qps导致系统崩溃,为了应对这一现象,衍生出了负载均衡,服务降级,缓存等技术,这里简单总结一下另一种保护系统的技术--限流。
限流,顾名思义,就是限制一段时间内的流量,使流量保持在可控范围之内,保障系统安全。限流有应用层面与软件层面之分。
软件限流-nginx
nginx作为广泛使用的负载均衡器,功能也是十分齐全,nginx提供了2个限流相关的模块
ngx_http_limit_conn_module:限制单位时间内的连接数,单一IP有效。
ngx_http_limit_req_module:限制同一时间q请求,对单一IP有效。
具体的如何配置可以点击连接查看官方文档。
应用层面
nginx的限流是第一道关卡,但只能对单一IP生效,所以在程序层面我们也要做限流。程序层面限流大致有以下几种算法。
1.计数器法
这个算法比较简单,设置一个计数器,计算一段时间内通过请求的数量,超过预定值则直接拒绝。这里的计数器需要使用java并发包中的cas类。算法缺点比较明显,比如我们限制一分钟内只能通过100次请求,那么前5秒内就已经请求100次了,那么后55秒内的请求只能拒绝,导致请求分配不均匀。
2.漏桶算法
算法原理如图,桶的容量是固定的,并且出水速度是固定的。假如限制一秒内只能通过10个请求,那么不管请求流量多大,处理速度是恒定的。但是如果桶容量很快就满了的话,新进来的请求就要被丢弃,假如短时间内进入大量请求导致桶满,那么后来一段时间内的请求只能拒绝,缺点和计数器算法比较类似。
3.令牌桶算法
这个算法是大家广泛采用的限流算法,很大程度上解决了上面俩种算法的缺点,原理是用一个桶存放固定数量的令牌,以固定的速度往桶里放令牌,每次调用需要先获取令牌。当桶中令牌达到上限,就丢弃新生成的令牌。该算法可以使请求速度分散并且允许一定程度的大量请求调用。
该算法已经有现成的类库做了封装--大名鼎鼎的guava。RateLimiter即为限流器类,下面是几个常用的方法,具体使用详见官方文档。
RateLimiter r = RateLimiter.create(10); //创建一个qps最大为10的限流器
r.acquire() // 表示阻塞直到获取一个令牌
r.tryAcquire() //如果没有可用令牌,就直接返回false,该方法可以指定超时时间。
集群限流
上面所说的都是单机层面的限流,若是集群的限流,我们一般使用Redis中来实现
最简单的做法就是,为某一个接口生成一个特定的key,并为其设置过期时间,在过期时间内每通过一次该接口就将其值原子性的加1。这个算法就是最简单的计算器算法了,哪我想用高级一点的算法,算法功底又差,又不想自己写该咋办?
嘿嘿嘿,Redis 4.0 提供了一个限流 Redis 模块名为redis-cell,有了他我们可以方便的实现限流了。该模块使用了漏斗算法,并提供了原子的限流指令。该模块只有一个指令,如下图。
摘自redis深度历险上图意味着漏水速率为每 60s 最多 30 次,漏斗的初始容量为 15。
具体的redis-cell模块如何安装这里贴出来:
Redis-Cell地址:https://github.com/brandur/redis-cell 按照相关文档进行编译安装
网友评论