实现亿级流量下的分布式限流 在互联网应用中,系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一、京东618、秒杀、抢购促销等,这些都是典型的大流量场景。系统架构解密,不是所有的秒杀都是秒杀!》
分布式限流需要解决什么问题呢?我想至少有下面几个:
1.动态规则:比如限流的QPS我们希望可以动态修改,限流的功能可以随时开启、关闭,限流的规则可以跟随业务进行动态变更等。
2.集群限流:比如对SpringCloud微服务架构中的某服务下的所有实例进行统一限流,以控制后续访问数据库的流量。
3.熔断降级:比如在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
可选的其它几个功能,诸如实时监控数据、网关流控、热点参数限流、系统自适应限流、黑白名单控制、注解支持等,这些功能其实可以非常方便的进行扩展。
分布式缓存
缓存更新模式
CacheAside,常用模式,应用要维护缓存的失效,命中,更新等动作
Read/WriteThrough,缓存代理更新数据库操作,应用视角只有一份存储
WriteBehindCache,IO加速方式之一,更新操作只在内测完成,异步进行批量更新数据库
其实不管处理何种场景,本质都是降低流量保证应用的高可用。
常见算法
对于限流常见有两种算法:
漏桶算法
令牌桶算法
漏桶算法比较简单,就是将流量放入桶中,漏桶同时也按照一定的速率流出,如果流量过快的话就会溢出(漏桶并不会提高流出速率)。溢出的流量则直接丢弃。
如下图所示:
这种做法简单粗暴。
漏桶算法虽说简单,但却不能应对实际场景,比如突然暴增的流量。
这时就需要用到令牌桶算法:
令牌桶会以一个恒定的速率向固定容量大小桶中放入令牌,当有流量来时则取走一个或多个令牌。当桶中没有令牌则将当前请求丢弃或阻塞。
相比之下令牌桶可以应对一定的突发流量。
RateLimiter实现
对于令牌桶的代码实现,可以直接使用Guava包中的RateLimiter。
- @Override
- public BaseResponse<UserResVO> getUserByFeignBatch(@RequestBody UserReqVO userReqVO) {
- //调用远程服务
- OrderNoReqVO vo = new OrderNoReqVO() ;
- vo.setReqNo(userReqVO.getReqNo());
- RateLimiter limiter = RateLimiter.create(2.0) ;
- //批量调用
- for (int i = 0 ;i< 10 ; i++){
- double acquire = limiter.acquire();
- logger.debug("获取令牌成功!,消耗=" + acquire);
- BaseResponse<OrderNoResVO> orderNo = orderServiceClient.getOrderNo(vo);
- logger.debug("远程返回:"+JSON.toJSONString(orderNo));
- }
- UserRes userRes = new UserRes() ;
- userRes.setUserId(123);
- userRes.setUserName("张三");
- userRes.setReqNo(userReqVO.getReqNo());
- userRes.setCode(StatusEnum.SUCCESS.getCode());
- userRes.setMessage("成功");
- return userRes ;
- }