使用Guava实现限流器

简介: 使用Guava实现限流器

为什么需要限流?


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

 

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

 

现有的方案


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

 

令牌桶算法


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

1.    假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中。

2.    桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝。

3.    当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上。

4.    如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。

 

限流器实现



1.pom文件中引入Guava

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.0</version>
</dependency>

 

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

a)定义一个拦截器抽象类,用于多个拦截器复用,主要是继承HandlerInterceptorAdapter,重写preHandle方法;并提供preFilter抽象方法,供子类实现。

image.png

 

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;
    }
}

使用Guava提供的RateLimiter类来实现流量控制,过程很简单:定义了一个QPS1的全局限流器(便于测试),使用tryAcquire()方法来尝试获取令牌,如果成功则返回ResponseEnum.OK,否则返回ResponseEnum.RATE_LIMIT

 

3.继承WebMvcConfigurerAdapter来添加自定义拦截器

image.png



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的枚举:

image.png

所有文件的目录结构如下:

image.png

 

5.使用Postman来测试接口


快速并且反复的调用接口,可以很容易的看到两种结果。

成功通过限流器的结果:

image.png

 

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

image.png


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

image.png


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

 

错误情况


如果在测试时,出现以下错误。

image.png


解决:在pom依赖中添加以下jar包即可解决。

 

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.3</version>
</dependency>

推荐阅读


字节、美团、快手核心部门面试总结(真题解析)

4 Java 经验,阿里网易拼多多面试总结、心得体会

面试阿里,HashMap 这一篇就够了

面试必问的 MySQL,你懂了吗?

面试必问的线程池,你懂了吗?

 

相关文章
|
8月前
重试工具Guava-Retryer
使用Guava Retrying库进行重试机制的示例:首先在项目中引入依赖,然后通过RetryerBuilder创建Retryer实例,设置重试条件(如:结果为null或出现特定异常),并配置停止策略和等待策略。代码示例展示了当模拟操作失败一定次数后,最终成功执行的情况。Guava Retrying提供多种重试条件,如基于异常或自定义断言,并支持配置重试次数和间隔时间。
134 5
|
6月前
|
监控 算法 Java
详解 Java 限流接口实现问题之避免令牌桶限流算法可能导致的过载问题如何解决
详解 Java 限流接口实现问题之避免令牌桶限流算法可能导致的过载问题如何解决
|
6月前
|
算法 Java
详解 Java 限流接口实现问题之漏桶限流算法的缺点问题如何解决
详解 Java 限流接口实现问题之漏桶限流算法的缺点问题如何解决
|
6月前
|
算法 Java UED
详解 Java 限流接口实现问题之滑动窗口限流算法的缺点如何解决
详解 Java 限流接口实现问题之滑动窗口限流算法的缺点如何解决
127 0
|
8月前
|
Java Maven
Guava RateLimiter单机实战指南
Guava RateLimiter单机实战指南
68 0
|
8月前
|
存储 缓存 NoSQL
Guava 缓存详解及使用
Guava Cache 是`Google Fuava`中的一个内存缓存模块,用于将数据缓存到JVM内存中。 本文主要介绍下Guava缓存的配置详解及相关使用 缓存分为本地缓存与分布式缓存。本地缓存为了保证线程安全问题,一般使用`ConcurrentMap`的方式保存在内存之中,而常见的分布式缓存则有`Redis`,`MongoDB`等。
|
缓存 算法 Java
Java实现系统限流
限流是保障系统高可用的方式之一,也是大厂高频面试题,如果面试官问一句,“如何实现每秒钟1000个请求的限流?”,你要是分分钟给他写上几种限流方案,那岂不香哉,哈哈!话不多说,我来列几种常用限流实现方式。
|
缓存 算法 API
【如何】guava的RateLimiter使用
【如何】guava的RateLimiter使用
112 0
|
Java
SpringBoot整合RateLimiter实现限流
写作缘由 在和某学长炫耀在自己会用Redis+Lua实现滑动窗口限流时,他说现在都用RateLimiter,所以就我就想搞个Demo,但是度娘了一下,感觉我搜索到的博客有几个个人认为不太完善的地方,比如只贴了部分代码,没贴依赖。尤其是你用AOP实现的时候,其实依赖哪个还有有讲究的;还有一个问题就是大多都是基于AOP实现,拦截器实现也是一个不错的方式,所以此处用拦截器HandlerInterceptorAdapter实现。
350 0
|
存储 缓存 NoSQL
详解Redisson分布式限流的实现原理
我们目前在工作中遇到一个性能问题,我们有个定时任务需要处理大量的数据,为了提升吞吐量,所以部署了很多台机器,但这个任务在运行前需要从别的服务那拉取大量的数据,随着数据量的增大,如果同时多台机器并发拉取数据,会对下游服务产生非常大的压力。之前已经增加了单机限流,但无法解决问题,因为这个数据任务运行中只有不到10%的时间拉取数据,如果单机限流限制太狠,虽然集群总的请求量控制住了,但任务吞吐量又降下来
466 0

热门文章

最新文章

下一篇
开通oss服务