【Java】四种方案实现限流

这篇具有很好参考价值的文章主要介绍了【Java】四种方案实现限流。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

方案一、固定窗口限流算法

这里我们通过一个 demo 来介绍固定窗口限流算法。

  1. 创建一个 FixWindowRateLimiterService 类。
@Service
public class FixWindowRateLimiterService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final DefaultRedisScript<Long> LIMIT_SCRIPT;


    /**
     * 是否运行请求通过
     *
     * @param key     Redis key
     * @param max     允许请求通过的最大数
     * @param timeout 一个窗口的时间
     * @return true:通过 false:限流
     */
    public boolean isAllowed(String key, Long max, Long timeout) {
        Long signal = stringRedisTemplate.execute(
                LIMIT_SCRIPT,
                Collections.singletonList(key),
                String.valueOf(max),
                String.valueOf(timeout)
        );

        if (Objects.isNull(signal)) {
            return false;
        }

        //返回 0,则说明就是限流
        return signal != 0;
    }

    static {
        LIMIT_SCRIPT = new DefaultRedisScript<>();
        LIMIT_SCRIPT.setLocation(new ClassPathResource("RateLimiterLua.lua"));
        LIMIT_SCRIPT.setResultType(Long.class);
    }
}
  1. 写一个 lua 脚本(保证线程安全),lua 脚本要放在 resources 的根目录下
local key = KEYS[1]
local max = tonumber(ARGV[1])
local timeout = tonumber(ARGV[2])
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > max then
    return 0
else
    redis.call("INCRBY", key, 1)
    redis.call("EXPIRE", key, timeout)
    return current + 1
end
  1. 测试
    java 限流方案,Java基础,限流
    注意,固定窗口算法存在边界问题,下图介绍了窗口问题。
    java 限流方案,Java基础,限流

方案二、滑动窗口限流算法

滑动窗口限流算法相对固定窗口限流算法更复杂一些,但更为精确。下面是一个基于 Redis 的 zset 结构 实现的简单滑动窗口限流的例子。

@RestController
public class RateLimitController {
    private static final String KEY = "rate:limit";
    private static final int MAX_VISIT = 1;

    @Resource
    private RedisTemplate redisTemplate;

    @ResponseBody
    @GetMapping("/visit")
    public String visit() {
        //1970-01-01T00:00:00Z 到现在的秒数
        long now = Instant.now().getEpochSecond();
        //移除现在这个时刻往前推 60s 的访问统计数
        redisTemplate.opsForZSet().removeRangeByScore(KEY, 0, now - 60);
        //获取当前的访问统计数
        Long currentVisits = redisTemplate.opsForZSet().zCard(KEY);

        if (currentVisits >= MAX_VISIT) {
            return "请稍后再试~";
        } else {
            redisTemplate.opsForZSet().add(KEY, String.valueOf(now), now);
            return "访问成功~";
        }
    }
}

方案三、漏桶限流算法

这个算法的基本思想就像有一个漏洞的桶一样。漏桶以一定的速度出水,当水流入过大的时候溢出。通过这个思想来进行流量的控制。

在程序实现中,漏桶通常以一个队列的形式存在,在有新的请求先进入队列中。队列以一定的速率处理请求,当队列满了以后,新进入的请求就会被拒绝。

下面是一个漏桶算法的 demo。在这个例子中,我们创建了一个容量为 10 的桶,每秒可以漏水两个(也就是系统每秒可以处理两个请求)。每当有新请求到来时,我们先计算桶里还剩多少水,如果没满则把请求加进去,满了就拒绝请求。

public class LeakyBucketDemo {
    /**
     * 桶的容量
     */
    private final long capacity = 10L;

    /**
     * 水流出的速度
     */
    private final long rate = 2L;

    /**
     * 当前水量(实际上就是请求书)
     */
    private long water = 0L;

    /**
     * 上次漏水时间
     */
    private long lastTime = System.currentTimeMillis();

    public boolean tryConsume() {
        long now = System.currentTimeMillis();
        //计算当前水量
        water = Math.max(0, water - (now - lastTime) * rate);
        lastTime = now;
        //判断剩余空间是否足够
        if ((water + 1) < capacity) {
            water++;
            return true;
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        LeakyBucketDemo leakyBucketDemo = new LeakyBucketDemo();
        for (int i = 0; i < 11; i++) {
            System.out.println(leakyBucketDemo.tryConsume() ? "请求通过" : "请求被限流");
        }
    }
} 

方案四、令牌桶限流算法

这个算法的思想是在一个常量固定速率下,把令牌放到令牌桶中。当请求来临的时候,令牌桶中有令牌则请求成功,没有令牌则请求失败。每请求成功一次,就会桶令牌丢到一个令牌。

下面是一个使用 Google 开源的 Guava 库来做限流算法的 demo。

public class TokenBucketDemo {
    private final RateLimiter rateLimiter = RateLimiter.create(10);

    public void doRequest() {
        if (rateLimiter.tryAcquire()) {
            System.out.println("正常处理请求");
        } else {
            System.out.println("限流");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TokenBucketDemo tokenBucketDemo = new TokenBucketDemo();
        for (int i = 0; i < 20; i++) {
            Thread.sleep(1000);
            tokenBucketDemo.doRequest();
        }
    }
}

总结

四种限流算法各有优缺点,需要根据自己的业务场景选择使用。推荐项目比较复杂的时候使用成熟的框架,比如sentinel。

  1. 固定窗口限流算法:适用于对精度要求不高,对性能要求比较高的场景。
  2. 滑动窗口限流算法:适用于对限流精度比较高的场景。
  3. 漏桶限流算法:适用于访问速率绝对稳定的场景。
  4. 令牌桶限流算法:适用于访问速率相对比较稳定的场景,但也可以应对一定的突发流量的场景。(对于使用 Guava 实现的方案,如果自己的项目是分布式的,那么此方案不适用 --Guava 是单机的)

最后

我是 xiucai,一位后端开发工程师。

如果你对我感兴趣,请移步我的个人博客,进一步了解。文章来源地址https://www.toymoban.com/news/detail-857946.html

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 本文首发于个人博客,未经许可禁止转载💌

到了这里,关于【Java】四种方案实现限流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JAVA:基于Redis 实现计数器限流

    1、简述 在现实世界中可能会出现服务器被虚假请求轰炸的情况,因此您可能希望控制这种虚假的请求。 一些实际使用情形可能如下所示: API配额管理-作为提供者,您可能希望根据用户的付款情况限制向服务器发出API请求的速率。这可以在客户端或服务端实现。 安全性-防止

    2024年02月02日
    浏览(42)
  • java基础:初始化ArrayList时直接赋值的四种方式

    在Java中,初始化ArrayList时直接赋值有以下几种常见方式: 构造器传入集合 : 或者在Java 9及以上版本中使用 List.of() 方法创建不可变列表: 使用匿名内部类 (不常用且可能引起混淆,实际编程中很少这样用): 注意:这种方式利用了匿名内部类的实例初始化块,但不是标准

    2024年04月22日
    浏览(42)
  • 【限流】4 种常见的限流实现方案

    在微服务应用中,考虑到技术栈的组合,团队人员的开发水平,以及易维护性等因素,一个比较通用的做法是,利用 AOP 技术 + 自定义注解实现 对特定的方法或接口进行限流。 下面基于这个思路来分别介绍下几种常用的限流方案的实现: 基于 guava 限流实现(单机版) 基于

    2024年02月15日
    浏览(38)
  • java进行系统的限流实现--Guava RateLimiter、简单计数、滑窗计数、信号量、令牌桶

    本文主要介绍了几种限流方法:Guava RateLimiter、简单计数、滑窗计数、信号量、令牌桶,漏桶算法和nginx限流等等 1、引入guava集成的工具 pom.xml 文件 demo代码实现 2.令牌桶算法 3、滑窗计数器 4、信号量

    2024年02月09日
    浏览(41)
  • 布隆过滤器四种实现(Java,Guava,hutool,Redisson)

    为预防大量黑客故意发起非法的时间查询请求,造成缓存击穿,建议采用布隆过滤器的方法解决。布隆过滤器通过一个很长的二进制向量和一系列随机映射函数(哈希函数)来记录与识别某个数据是否在一个集合中。如果数据不在集合中,能被识别出来,不需要到数据库中进

    2024年01月16日
    浏览(41)
  • Java 实现 HTTP 请求的四种方式,你都学会了么?

    前言 在日常工作和学习中,有很多地方都需要发送HTTP请求,本文以Java为例,总结发送HTTP请求的多种方式 HTTP请求实现过程 GET 创建远程连接 设置连接方式(get、post、put…) 设置连接超时时间 设置响应读取时间 发起请求 获取请求数据 关闭连接 POST 创建远程连接 设置连接方

    2024年02月08日
    浏览(48)
  • 服务限流实现方案

    服务限流怎么做 限流算法 计数器 每个单位时间能通过的请求数固定,超过阈值直接拒绝。 通过维护一个单位时间内的计数器,每次请求计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被绝,直到单位时间已经过去,再将计数器重置为零。但可能会

    2024年01月23日
    浏览(42)
  • 分布式限流方案及实现

    优质博文:IT-BLOG-CN 限流是对高并发访问进行限制,限速的过程。通过限流来限制资源,可以提高系统的稳定性和可靠性,控制系统的负载,削峰填谷,保证服务质量。 服务限流后的常见处理方式: 【1】拒绝服务; 【2】排队或等待; 【3】服务降级(当服务器压力剧增的情

    2024年02月14日
    浏览(36)
  • 基于粒子群优化算法(PSO)的Matlab、Python、Java、C++四种仿真实现(附上多个完整仿真源码)

    9.1 泛型的概述和好处 泛型 :是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是 参数化类型 ,也就是说所操作的数据类型被指定为一个参数 一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。

    2024年02月15日
    浏览(69)
  • 【微服务】springboot 通用限流方案设计与实现

    目录 一、背景 二、限流概述 2.1 dubbo 服务治理模式 2.1.1 dubbo框架级限流 2.1.2 线程池设

    2024年02月10日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包