副问题[/!--empirenews.page--]
媒介
在一个高并发体系中对流量的把控长短常重要的,当庞大的流量直接哀求到我们的处事器上没多久就也许造成接口不行用,不处理赏罚的话乃至会造成整个应用不行用。
好比最近就有个这样的需求,我作为客户端要向kafka出产数据,而kafka的斲丧者则再绵绵不断的斲丧数据,并将斲丧的数据所有哀求到web处事器,虽说做了负载(有4台web处事器)但营业数据的量也是庞大的,每秒钟也许有上万条数据发生。假如出产者直接出产数据的话极有也许把web处事器拖垮。

对此就必必要做限流处理赏罚,每秒钟出产必然限额的数据到kafka,这样就能极洪流平的担保web的正常运转。
着实不管处理赏罚何种场景,本质都是低落流量担保应用的高可用。
常见算法
对付限流常见有两种算法:
漏桶算法较量简朴,就是将流量放入桶中,漏桶同时也凭证必然的速度流出,假如流量过快的话就会溢出(漏桶并不会进步流出速度)。溢出的流量则直接扬弃。
如下图所示:

这种做法简朴粗暴。
漏桶算法虽说简朴,但却不能应对现实场景,好比溘然暴增的流量。
这时就必要用到令牌桶算法:
令牌桶会以一个恒定的速度向牢靠容量巨细桶中放入令牌,当有流量来时则取走一个或多个令牌。当桶中没有令牌则将当前哀求扬弃或阻塞。
对比之命令牌桶可以应对必然的突发流量。
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 ;
- }
详见此。
挪勤奋效如下:
代码可以看出以每秒向桶中放入两个令牌,哀求一次耗损一个令牌。以是每秒钟只能发送两个哀求。凭证图中的时刻来看也确实云云(返回值是获取此令牌所耗损的时刻,差不多也是每500ms一个)。
行使RateLimiter有几个值得留意的处所:
应承先斲丧,后付款,意思就是它可以来一个哀求的时辰一次性取走几个可能是剩下全部的令牌乃至多取,可是后头的哀求就得为上一次哀求买单,它必要守候桶中的令牌补齐之后才气继承获取令牌。
总结
针对付单个应用的限流 RateLimiter 够用了,假如是漫衍式情形可以借助 Redis 来完成。
来做演示。
在 Order 应用提供的接口中采纳了限流。起首是设置了限流器材的 Bean:
- @Configuration
- public class RedisLimitConfig {
- @Value("${redis.limit}")
- private int limit;
- @Autowired
- private JedisConnectionFactory jedisConnectionFactory;
- @Bean
- public RedisLimit build() {
- RedisClusterConnection clusterConnection = jedisConnectionFactory.getClusterConnection();
- JedisCluster jedisCluster = (JedisCluster) clusterConnection.getNativeConnection();
- RedisLimit redisLimit = new RedisLimit.Builder<>(jedisCluster)
- .limit(limit)
- .build();
- return redisLimit;
- }
- }
接着在 Controller 行使组件:
- @Autowired
- private RedisLimit redisLimit ;
- @Override
- @CheckReqNo
- public BaseResponse<OrderNoResVO> getOrderNo(@RequestBody OrderNoReqVO orderNoReq) {
- BaseResponse<OrderNoResVO> res = new BaseResponse();
- //限流
- boolean limit = redisLimit.limit();
- if (!limit){
- res.setCode(StatusEnum.REQUEST_LIMIT.getCode());
- res.setMessage(StatusEnum.REQUEST_LIMIT.getMessage());
- return res ;
- }
- res.setReqNo(orderNoReq.getReqNo());
- if (null == orderNoReq.getAppId()){
- throw new SBCException(StatusEnum.FAIL);
- }
- OrderNoResVO orderNoRes = new OrderNoResVO() ;
- orderNoRes.setOrderId(DateUtil.getLongTime());
- res.setCode(StatusEnum.SUCCESS.getCode());
- res.setMessage(StatusEnum.SUCCESS.getMessage());
- res.setDataBody(orderNoRes);
- return res ;
- }
(编辑:湖南网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|