java进行系统的限流实现--Guava RateLimiter、简单计数、滑窗计数、信号量、令牌桶

这篇具有很好参考价值的文章主要介绍了java进行系统的限流实现--Guava RateLimiter、简单计数、滑窗计数、信号量、令牌桶。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

demo代码实现

package com.znkeji.zn_wifi_carck.guava;

import com.google.common.util.concurrent.RateLimiter;

public class GuavaRateLimiterTest {

    //比如每秒生产10个令牌,相当于每100ms生产1个令牌
    //RateLimiter是Guava库中的一个限流器
    // 基于PPS进行限流
    //基于PPS限流的同时提供热启动
    private RateLimiter rateLimiter=RateLimiter.create(10);


    /**
     * 模拟执行业务方法
     */

    public void exeBiz(){

        if (rateLimiter.tryAcquire(1)){///返回boolean 尝试获取许可,如果该许可可以在无延迟下的情况下立即获取得到的话
            try {
                Thread.sleep(50);
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+":执行业务逻辑");
        }else {
            System.out.println("线程"+Thread.currentThread().getName()+":被限流了");
        }

    }


    public static void main(String[] args) throws InterruptedException {

        GuavaRateLimiterTest guavaRateLimiterTest = new GuavaRateLimiterTest();
        System.out.println("线程开始等待");
        Thread.sleep(500); //等待500ms,让limiter生产一些令牌
        System.out.println("线程睡了0.5秒");
        //模拟瞬间生产100个线程请求
        for (int i=0;i<100;i++){
            new Thread(guavaRateLimiterTest::exeBiz).start();
        }

    }





}

2.令牌桶算法

package com.znkeji.zn_wifi_carck.guava;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * 令牌桶算法:一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,如有剩余容量则添加,没有则放弃。如果有请求进来,则需要先从桶里获取令牌,当桶里没有令牌可取时,则拒绝任务。
 *
 * 令牌桶的优点是:可以改变添加令牌的速率,一旦提高速率,则可以处理突发流量。
 */
public class TokenBucket {

    /**
     * 定义的桶
     */
    public class Bucket {
        //容量
        int capacity;
        //速率,每秒放多少
        int rateCount;
        //目前token个数
        AtomicInteger curCount = new AtomicInteger(0);

        public Bucket(int capacity, int rateCount) {
            this.capacity = capacity;
            this.rateCount = rateCount;
        }

        public void put() {
            if (curCount.get() < capacity) {
                System.out.println("目前数量==" + curCount.get() + ", 我还可以继续放");
                curCount.addAndGet(rateCount);
            }
        }

        public boolean get() {
            if (curCount.get() >= 1) {
                curCount.decrementAndGet();
                return true;
            }
            return false;
        }
    }

//    @Test
    public void testTokenBucket() throws InterruptedException {

        Bucket bucket = new Bucket(5, 2);

        //固定线程,固定的速率往桶里放数据,比如每秒N个
        ScheduledThreadPoolExecutor scheduledCheck = new ScheduledThreadPoolExecutor(1);
        scheduledCheck.scheduleAtFixedRate(() -> {
            bucket.put();
        }, 0, 1, TimeUnit.SECONDS);

        //先等待一会儿,让桶里放点token
        Thread.sleep(6000);

        //模拟瞬间10个线程进来拿token
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                if (bucket.get()) {
                    System.out.println(Thread.currentThread() + "获取到了资源");
                } else {
                    System.out.println(Thread.currentThread() + "被拒绝");
                }
            }).start();
        }

        //等待,往桶里放token
        Thread.sleep(3000);

        //继续瞬间10个线程进来拿token
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                if (bucket.get()) {
                    System.out.println(Thread.currentThread() + "获取到了资源");
                } else {
                    System.out.println(Thread.currentThread() + "被拒绝");
                }
            }).start();
        }
    }
}

3、滑窗计数器

package com.znkeji.zn_wifi_carck.guava;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 滑窗计数器  打个比方,某接口每秒允许100个请求,设置一个滑窗,窗口中有10个格子,每个格子占100ms,每100ms移动一次。滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。
 */
public class SliderWindowRateLimiter implements Runnable {

    //每秒允许的最大访问数
    private final long maxVisitPerSecond;
    //将每秒时间划分N个块
    private final int block;
    //每个块存储的数量
    private final AtomicLong[] countPerBlock;
    //滑动窗口划到了哪个块儿,可以理解为滑动窗口的起始下标位置
    private volatile int index;
    //目前总的数量
    private AtomicLong allCount;


    /**
     * 构造函数
     *
     * @param block,每秒钟划分N个窗口
     * @param maxVisitPerSecond 每秒最大访问数量
     */

    public SliderWindowRateLimiter(int block,long maxVisitPerSecond){
        this.block=block;
        this.maxVisitPerSecond=maxVisitPerSecond;
        countPerBlock= new AtomicLong[block];
        for (int i=0;i<block;i++){
            countPerBlock[i]=new AtomicLong();
        }
        allCount=new AtomicLong(0);
    }

    /**
     * 判断是否超过最大允许数量
     *
     * @return
     */
    public boolean isOverLimit() {
        return currentQPS() > maxVisitPerSecond;
    }


    /**
     * 获取目前总的访问数
     *
     * @return
     */
    public long currentQPS() {
        return allCount.get();
    }

    /**
     * 请求访问进来,判断是否可以执行业务逻辑
     */
    public void visit(){
        countPerBlock[index].incrementAndGet();
        allCount.incrementAndGet();
        if (isOverLimit()){
            System.out.println(Thread.currentThread().getName()+"被限流了,currentQPS:"+currentQPS());
        }else {
            System.out.println(Thread.currentThread().getName()+"执行业务逻辑,currentQPS:"+currentQPS());
        }
    }



    /**
     * 定时执行器,
     * 每N毫秒滑块移动一次,然后再设置下新滑块的初始化数字0,然后新的请求会落到新的滑块上
     * 同时总数减掉新滑块上的数字,并且重置新的滑块上的数量
     */
    @Override
    public void run() {
        index=(index+1)%block;
        long val= countPerBlock[block].getAndSet(0);
        allCount.addAndGet(-val);
    }

    public static void main(String[] args) {
        SliderWindowRateLimiter sliderWindowRateLimiter = new SliderWindowRateLimiter(10, 100);

        //固定的速率移动滑块
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(sliderWindowRateLimiter, 100, 100, TimeUnit.MILLISECONDS);

        //模拟不同速度的请求
        new Thread(() -> {
            while (true) {
                sliderWindowRateLimiter.visit();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //模拟不同速度的请求
        new Thread(() -> {
            while (true) {
                sliderWindowRateLimiter.visit();
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

4、信号量文章来源地址https://www.toymoban.com/news/detail-696347.html

package com.znkeji.zn_wifi_carck.guava;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;

/**
 * 利用Semaphore,每隔固定速率,释放Semaphore的资源。线程获取到资源,则执行业务代码。
 */
public class SemaphoreOne {

    private static Semaphore semaphore = new Semaphore(10);

    public static void bizMethod() throws InterruptedException {
        if (!semaphore.tryAcquire()) {
            System.out.println(Thread.currentThread().getName() + "被拒绝");
            return;
        }

        System.out.println(Thread.currentThread().getName() + "执行业务逻辑");
        Thread.sleep(500);//模拟处理业务逻辑需要1秒
        semaphore.release();
    }

    public static void main(String[] args) {

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                semaphore.release(10);
                System.out.println("释放所有锁");
            }
        }, 1000, 1000);

        for (int i = 0; i < 10000; i++) {
            try {
                Thread.sleep(10);//模拟每隔10ms就有1个请求进来
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(() -> {
                try {
                    SemaphoreOne.bizMethod();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

到了这里,关于java进行系统的限流实现--Guava RateLimiter、简单计数、滑窗计数、信号量、令牌桶的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Java的guava 限流写法

    就这么简单!

    2024年02月11日
    浏览(30)
  • Guava RateLimiter预热模型

    本文已收录至我的个人网站:程序员波特,主要记录Java相关技术系列教程,共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源,让想要学习的你,不再迷茫。 我们都知道在做运动之前先得来几组拉伸之类的动作,给身体做个热身,让我们的身体平滑过渡到

    2024年01月18日
    浏览(33)
  • Guava-RateLimiter详解

    简介:  常用的限流算法有漏桶算法和令牌桶算法,guava的RateLimiter使用的是令牌桶算法,也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应,获取的方式有两种:阻塞等待令牌或

    2024年02月06日
    浏览(38)
  • 【springboot】spring的Aop结合Redis实现对短信接口的限流

    场景: 为了限制短信验证码接口的访问次数,防止被刷,结合Aop和redis根据用户ip对用户限流 首先我们创建一个 Spring Boot 工程,引入 Web 和 Redis 依赖,同时考虑到接口限流一般是通过注解来标记,而注解是通过 AOP 来解析的,所以我们还需要加上 AOP 的依赖,最终的依赖如下:

    2024年02月05日
    浏览(47)
  • Sentinel的限流和Gateway的限流差别?

    问题说明:考察对限流算法的掌握情况 限流算法常见的有三种实现:滑动时间窗口,令牌桶算法,漏桶算法。gateway则采用基于Redis实现的令牌桶算法。但是我们不会去用,而Sentinel 功能比较丰富。 而sentinel内部比较复杂: 默认限流模式是基于滑动时间窗口算法 针对资源做统

    2024年02月09日
    浏览(37)
  • SpringBoot利用Guava实现单机app限流访问

    物料准备: 1.引入Guava依赖 2.定义一个限流注解作用于api接口的方法上 3.定义一个切面,对注解标注的方法实现一个限流访问 使用方法,把@RateLimitAspect 注解添加到需要限流的API接口上即可,如 添加到@GetMapping 或 @PostMapping上

    2024年02月11日
    浏览(37)
  • 【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(基础编程篇)

    Preconditions(前置条件):让方法调用的前置条件判断更简单 。 Guava在Preconditions 类中提供了若干前置条件判断的实用方法,我们强烈建议在 Eclipse 中静态导入这些方法。每个方法都有三个变种: 当方法没有额外参数时,抛出的异常中不包含错误消息,这会使得调用方很难确

    2024年02月07日
    浏览(78)
  • 限流器 github的ratelimiter

    2024年02月22日
    浏览(37)
  • DRF的限流组件(源码分析)

    限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷。 对于匿名用户,使用用户IP作为唯一标识。 对于登录用户,使用用户ID或名称作为唯一标识。 局部配置(views) 全局配置(settings) 本质,每个限流的类中都有一个 all

    2023年04月21日
    浏览(31)
  • 基于 ActionFilters 的限流库DotNetRateLimiter使用

    在构建API项目时,有时出于安全考虑,防止访问用户恶意攻击,希望限制此用户ip地址的请求次数,减轻拒绝服务攻击可能性,也称作限流。接下来,我们就来学习开源库 DotNetRateLimiter 如何轻松实现限流。 安装Nuget包 在新建立的 WebAPI 项目中,通过 Nuget 包管理器安装 DotNetR

    2024年02月08日
    浏览(32)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包