Google guava第二讲:Ratelimiter限流原理与实现

本文是Google guava第二讲:Ratelimiter 限流原理与实现

文章目录

      • 1、为什么做限流?
      • 2、限流算法
      • 3 限流实战
        • 3.1、令牌桶算法
        • 3.2、限流器实现
      • 4、断路器原理与实现

1、为什么做限流?

  • 在开发高并发系统时,有三把利器用来保护系统:缓存、降级和限流。限流可以认为服务降级的一种,限流通过限制请求的流量以达到保护系统的目的。

  • 一般来说,系统的吞吐量是可以计算出一个阈值的,为了保证系统的稳定运行,一旦达到这个阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。否则,很容易导致服务器的宕机。

使用场景:

  • 1、数据迁移和修复的工作:例如我做过的:协议续签、商品联动协议状态
  • 2、大批量数据操作:例如:一键审核

开源软件中限流的设计

  • Nginx中限制瞬时并发连接数的limit_conn 模块,限制每秒平均速度的limit_req模块

2、限流算法

1、计数器算法

  • 原理:维护一个 counter变量,规定在单位时间内counter的大小不能超过最大值,每隔固定时间就将counter的值置为零。counter超过阈值就拒绝服务。

2、漏桶算法

  • 原理:维护一个固定容量的桶,按照指定的速度漏水,如果桶空了,就会停止漏水,如果桶满了,就会忽略后面来的请求

  • 效果:当请求过多时,队列中的请求就开始堆积,当队列满后,系统就会开始拒绝服务。

  • 具体流程如下图Google guava第二讲:Ratelimiter限流原理与实现_第1张图片

3、令牌桶算法

  • 随着时间流逝,系统按指定速度往桶里添加token,每来一个新请求,就从桶里拿走一个token,如果没有token就拒绝服务
  • 可以控制系统的处理速度
  • 令牌桶流程如下所示
    Google guava第二讲:Ratelimiter限流原理与实现_第2张图片

3 限流实战

现有的方案

  • Google的Guava工具包中就提供了一个限流工具类——RateLimiter,本文也是通过使用该工具类来实现限流功能。RateLimiter是基于“令牌通算法”来实现限流的。

3.1、令牌桶算法

令牌桶算法是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。令牌桶算法基本可以用下面的几个概念来描述:

  • 1、假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中。
  • 2、桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝。
  • 3、当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上。
  • 4、如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。

3.2、限流器实现

1、pom文件中引入Guava包

<dependency>
    <groupId>com.google.guavagroupId>
    <artifactId>guavaartifactId>
    <version>23.0version>
dependency>

2、自定义拦截器,并在拦截器中实现限流

a)定义一个拦截器抽象类,用于多个拦截器复用,主要是继承 HandlerInterceptorAdapter,重写preHandle方法;并提供preFilter抽象方法,供子类实现。
Google guava第二讲:Ratelimiter限流原理与实现_第3张图片
b)定义流量控制拦截器,流量控制拦截器继承自上面的拦截器抽象类,在preFilter方法中进行流量控制。

@Component("rateLimitInterceptor")
public class RateLimitInterceptor extends AbstractInterceptor {
    /**
     * 单机全局限流器(限制QPS为1)
     */
    private static final RateLimiter rateLimiter = RateLimiter.create(1);
    @Override
    protected ResponseEnum preFilter(HttpServletRequest request) {
        if (!rateLimiter.tryAcquire()) {
            System.out.println("限流中......");
            return ResponseEnum.RATE_LIMIT;
        }
        System.out.println("请求成功");
        return ResponseEnum.OK;
    }
}

3、继承WebMvcConfigurerAdapter来添加自定义拦截器
Google guava第二讲:Ratelimiter限流原理与实现_第4张图片
4、写一个Controller来提供一个简单的访问接口

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserPOMapper userPOMapper;
    @RequestMapping("getUserList")
    @ResponseBody
    public ResponseDTO getUserList() {
        ResponseDTO responseDTO = new ResponseDTO();
        responseDTO.setCode(ResponseEnum.OK.getCode());
        UserPOExample userPOExample = new UserPOExample();
        UserPOExample.Criteria criteria = userPOExample.createCriteria();
        // and Name = 'admin'
        criteria.andNameEqualTo("admin");
        criteria.andPasswordEqualTo("admin");
        // and Age Between 24, 26
        criteria.andAgeBetween(24, 26);
        try {
            List<UserPO> userPOList = userPOMapper.selectByExample(userPOExample);
            responseDTO.setContent(userPOList);
            return responseDTO;
        } catch (Exception e) {
            responseDTO.setCode(ResponseEnum.QUERY_USER_FAILED.getCode());
            responseDTO.setMsg(ResponseEnum.QUERY_USER_FAILED.getMsg());
            return responseDTO;
        }
    }
}

上文使用到的ResponseEnum是一个返回Code的枚举:
Google guava第二讲:Ratelimiter限流原理与实现_第5张图片

5、使用RestLet来测试接口
快速并且反复的调用接口,可以很容易的看到两种结果。

成功通过限流器的结果:

  • todo

  • 没有成功通过限流器的返回结果:

  • todo 补充

反复调用时,Console输出如下:

请求成功
请求成功
限流中。。。
请求成功
限流中。。。

至此,简单的限流器实现完成。

4、断路器原理与实现

1、Hystrix (NetFlix出品)

2、Sentinel (阿里巴巴出品)
sentinel见这篇文章:开发工具篇第五讲

你可能感兴趣的:(常用开发工具,guava,面试,限流,RateLimiter)