iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java限流实现的几种方法详解
  • 350
分享到

Java限流实现的几种方法详解

Java限流Java限流算法Java限流方案 2022-12-03 22:12:14 350人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录计数器信号量滑动窗口漏桶令牌桶测试示例代码计数器 计数器限流方式比较粗暴,一次访问就增加一次计数,在系统内设置每 N 秒的访问量,超过访问量的访问直接丢弃,从而实现限流访问。 具

计数器

计数器限流方式比较粗暴,一次访问就增加一次计数,在系统内设置每 N 秒的访问量,超过访问量的访问直接丢弃,从而实现限流访问。

具体大概是以下步骤:

  • 将时间划分为固定的窗口大小,例如 1 s;
  • 在窗口时间段内,每来一个请求,对计数器加 1;
  • 当计数器达到设定限制后,该窗口时间内的后续请求都将被丢弃;
  • 该窗口时间结束后,计数器清零,从新开始计数。

这种算法的弊端

在开始的时间,访问量被使用完后,1 s 内会有很长时间的真空期是处于接口不可用的状态的,同时也有可能在一秒内出现两倍的访问量。

T窗口的前1/2时间 无流量进入,后1/2时间通过5个请求;

  • T+1窗口的前 1/2时间 通过5个请求,后1/2时间因达到限制丢弃请求。
  • 因此在 T的后1/2和(T+1)的前1/2时间组成的完整窗口内,通过了10个请求。

代码实现

 private final Semaphore count = new Semaphore(5);
 @PostConstruct
    public void init() {
        //初始化定时任务线程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 每10s执行5次
        service.scheduleAtFixedRate(() -> count.release(5), 10, 10, TimeUnit.SECONDS);
  }
 	
    public void count() {
        try {
            count.acquire();
            System.out.println("count");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

信号量

控制并发访问量

具体大概是以下步骤:

  • 初始化信号量
  • 每个请求获取信号量,请求完释放

代码实现

	private final Semaphore flag = new Semaphore(5);
	
    public void flag() {
        try {
            flag.acquire();
            System.out.println("flag");
            int i = new Random().nextInt(10);
            TimeUnit.SECONDS.sleep(i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            flag.release();
        }
    }

滑动窗口

具体大概是以下步骤:

  • 将时间划分为细粒度的区间每个区间
  • 维持一个计数器,每进入一个请求则将计数器加一;
  • 多个区间组成一个时间窗口,每流逝一个区间时间后,则抛弃最老的一个区间,纳入新区间。如图中示例的窗口 T1 变为窗口 T2;
  • 若当前窗口的区间计数器总和超过设定的限制数量,则本窗口内的后续请求都被丢弃。

代码实现

  private final AtomicInteger[] window = new AtomicInteger[10];
 @PostConstruct
    public void init() {
        //初始化定时任务线程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 10个窗口,每次滑动1s
        Arrays.fill(window, new AtomicInteger(0));
        service.scheduleAtFixedRate(() -> {
            int index = (int) (System.currentTimeMillis() / 1000 % 10);
            window[index] = new AtomicInteger(0);
        }, 1, 1, TimeUnit.SECONDS);
}
 	
    public void window() {
        int sum = 0;
        for (int i = 0; i < window.length; i++) {
            sum += window[i].get();
        }
        if (sum > 10) {
            return;
        }
        System.out.println("window");
        int index = (int) (System.currentTimeMillis() / 1000 % 10);
        window[index].getAndAdd(1);
    }

漏桶

具体大概是以下步骤:

  • 初始化一个队列,做桶
  • 每个请求入队列,队列满则阻塞
  • 启动定时任务,以固定的速率执行,执行时判读一下入队时间,如果延迟太久,直接丢弃(有可能客户端已经超时,服务端还没有处理)

代码实现

 private final BlockingQueue<Long> queue = new LinkedBlockingDeque<>(5);
  @PostConstruct
    public void init() {
        //初始化定时任务线程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 一恒定的速率执行
        service.scheduleAtFixedRate(() -> {
            try {
                if (System.currentTimeMillis() - queue.take() > 1000L) {
                    process();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 100, 100, TimeUnit.MILLISECONDS);
}
	
    public void bucket() {
        try {
            queue.put(System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  private void process() {
        System.out.println("process");
    }

令牌桶

令牌桶算法是漏斗算法的改进版,为了处理短时间的突发流量而做了优化,令牌桶算法主要由三部分组成:令牌流、数据流、令牌桶。

名词释义:

  • 令牌桶:流通令牌的管道,用于生成的令牌的流通,放入令牌桶中。
  • 数据流:进入系统的数据流量。
  • 令牌桶:保存令牌的区域,可以理解为一个缓冲区,令牌保存在这里用于使用。

具体大概是以下步骤:

  • 初始化一个队列做桶,大小为通的大小
  • 启动定时任务,以一定的速率往队列中放入令牌
  • 每个请求来临,去队列中获取令牌,获取成功正执行,否则阻塞

代码实现

private final BlockingQueue<Integer> token = new LinkedBlockingDeque<>(5);
  @PostConstruct
    public void init() {
        //初始化定时任务线程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 以恒定的速率放入令牌
        service.scheduleAtFixedRate(() -> {
            try {
                token.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
    public void token() {
        try {
            token.take();
            System.out.println("token");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

测试

  @Resource
    private LimitDemo demo;
    @Test
    public void count() throws InterruptedException {
        process(() -> demo.count());
    }
    @Test
    public void flag() throws InterruptedException {
        process(() -> demo.flag());
    }
    @Test
    public void window() throws InterruptedException {
        process(() -> demo.window());
    }
    @Test
    public void bucket() throws InterruptedException {
        process(() -> demo.bucket());
    }
    @Test
    public void token() throws InterruptedException {
        process(() -> demo.token());
    }
    private void process(Process process) throws InterruptedException {
        CompletableFuture<?>[] objects = IntStream.range(0, 10).mapToObj(i -> CompletableFuture.runAsync(() -> {
            while (true) {
                process.execute();
            }
        })).collect(Collectors.toList()).toArray(new CompletableFuture<?>[] {});
        CompletableFuture.allOf(objects);
        new CountDownLatch(1).await();
    }
    @FunctionalInterface
    public interface Process {
        void execute();
    }

示例代码

源码地址 https://GitHub.com/GoogalAmbition/googol/tree/master/limit

到此这篇关于Java限流实现的几种方法详解的文章就介绍到这了,更多相关Java限流内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java限流实现的几种方法详解

本文链接: https://www.lsjlt.com/news/173990.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java限流实现的几种方法详解
    目录计数器信号量滑动窗口漏桶令牌桶测试示例代码计数器 计数器限流方式比较粗暴,一次访问就增加一次计数,在系统内设置每 N 秒的访问量,超过访问量的访问直接丢弃,从而实现限流访问。 具...
    99+
    2022-12-03
    Java限流 Java限流算法 Java限流方案
  • 详解Golang实现请求限流的几种办法
    简单的并发控制 利用 channel 的缓冲设定,我们就可以来实现并发的限制。我们只要在执行并发的同时,往一个带有缓冲的 channel 里写入点东西(随便写啥...
    99+
    2024-04-02
  • Redis限流的几种实现
    目录一、简单的限流 基本原理 二、漏斗限流 基本原理Redis-Cell 参考来源 一、简单的限流 基本原理 当系统处理能力有限,如何组织计划外的请求对系统施压。首先我们先看下一...
    99+
    2024-04-02
  • Java实现5种限流算法及7种限流方式
    目录前言1. 限流2. 固定窗口算法2.1. 代码实现3. 滑动窗口算法3.1. 代码实现4. 滑动日志算法4.1. 代码实现5. 漏桶算法6. 令牌桶算法6.1. 代码实现6.2....
    99+
    2022-11-13
    Java 限流算法 Java 限流方式 Java 限流
  • 详解Redis实现限流的三种方式
     面对越来越多的高并发场景,限流显示的尤为重要。       当然,限流有许多种实现的方式,Redis具有很强大的功能,我用Redis实践了三...
    99+
    2024-04-02
  • Java服务限流算法的6种实现
    目录固定窗口算法滑动窗口算法漏桶算法令牌桶算法中间件限流网关限流总结服务限流,是指通过控制请求的速率或次数来达到保护服务的目的,在微服务中,我们通常会将它和熔断、降级搭配在一起使用,...
    99+
    2023-05-20
    Java服务限流 服务限流
  • 详解5种Java中常见限流算法
    目录01固定窗口02滑动窗口03漏桶算法04令牌桶05滑动日志06分布式限流07总结1.瞬时流量过高,服务被压垮? 2.恶意用户高频光顾,导致服务器宕机? 3.消息消费过快,导致数据...
    99+
    2023-05-14
    Java常见限流算法 Java限流算法 Java限流
  • Java 中分形图的几种方法详解
    Java分形Java的分形主要有一下几种:类似Clifford的分形。这种分形的特点是:分形的初始坐标为(0,0),通过初始坐标经过大量的迭代,得到一系列的点,根据得到的点来绘制分形曲线。这类分形的参数有限,可以很简单的实现。类似IFS f...
    99+
    2023-05-31
    java 分形 ava
  • 详解Java 打印堆栈的几种方法
    java 中可以通过 eclipse 等工具直接打印堆栈,但是对于某些环境中无法使用 eclipse 工具时,需要知道堆栈,如何处理呢?介绍3种方法供选择:方法一:package name.xu;public class CallStack...
    99+
    2023-05-31
    java 打印 堆栈
  • C++单例模式的几种实现方法详解
    目录局部静态变量方式静态成员变量指针方式智能指针方式辅助类智能指针单例模式通用的单例模板类总结局部静态变量方式 //通过静态成员变量实现单例 //懒汉式 class Single2 ...
    99+
    2024-04-02
  • Java线程池的几种实现方法和区别介绍实例详解
    下面通过实例代码为大家介绍Java线程池的几种实现方法和区别:import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.ArrayList;...
    99+
    2023-05-31
    java 线程池 ava
  • Redis实现限流器的三种方法(小结)
    目录方法一:基于Redis的setnx的操作方法二:基于Redis的数据结构zset方法三:基于Redis的令牌桶算法方法一:基于Redis的setnx的操作 我们在使用Redis的...
    99+
    2024-04-02
  • 使用Java实现Redis限流的方法
    1、概述   限流的含义是在单位时间内确保发往某个模块的请求数量小于某个数值,比如在实现秒杀功能时,需要确保在10秒内发往支付模块的请求数量小于500个。限流...
    99+
    2024-04-02
  • java实现异步的方法有哪几种
    在Java中实现异步的方法有多种方式,其中一些常见的包括: 使用线程池:通过创建一个线程池来处理异步任务,可以使用Executo...
    99+
    2024-04-02
  • java固定大小队列的几种实现方式详解
    目录前言基于Hutool中的FixedLinkedHashMap基于Guava的EvictingQueue基于Redis的list操作总结前言 最近团队有同学在开发中,遇到一个需求,...
    99+
    2024-04-02
  • Java分页查询的几种实现方法
    最近在做需求的过程中遇到几个需要分页查询的需求,我也对分页有了较深的了解。分页一般分为两种一种是直接在sql中分页,一种是在内存中分页。本文仅针对内存中分页做记录。 第一种    如果是一个查询接口,向接口中传入page,和size(p...
    99+
    2023-09-01
    java 数据库 mysql
  • Golang官方限流器库实现限流示例详解
    目录前言例子实现小结前言 在翻Golang官方库的过程中,发现一个有趣的库golang.org/x/time ,里面只有一个类rate,研究了一下发现它是一个限流器,实现了很多的功能...
    99+
    2024-04-02
  • 详解Oracle 中实现数据透视表的几种方法
    数据透视表(Pivot Table)是 Excel 中一个非常实用的分析功能,可以用于实现复杂的数据分类汇总和对比分析,是数据分析师和运营人员必备技能之一。今天我们来谈谈如何在 Or...
    99+
    2024-04-02
  • Java中List使用stream流转成map的几种方式详解
    目录实体例子List 转成Map<String,Object>List 转成Map<String,String>List 转成Map<String,Li...
    99+
    2023-05-16
    java stream list转map java list转map的三种方法 stream list转map
  • java 多线程的几种实现方法总结
    java 多线程的几种实现方法总结1.多线程有几种实现方法?同步有几种实现方法?多线程有两种实现方法,分别是继承Thread类与实现Runnable接口同步的实现方面有两种,分别是synchronized,wait与noti...
    99+
    2023-05-30
    java 多线程 ava
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作