限流器设计思路(浅入门)

限流器(Rate Limiter)是一种用于控制系统资源利用率和质量的重要机制。它通过限制单位时间内可以执行的操作数量,从而防止系统过载和保护服务的可靠性。在程序设计中,可以使用多种方式来实现限流器,下面是几个常见方案的介绍:

  • 令牌桶算法

  • 漏桶算法

  • 划窗算法

  • 固定窗口算法(缺点很大!)

  • 基于计数器的流量控制算法

  • ...

令牌桶算法(Token Bucket)

令牌桶算法是一种常见的限流实现方式。它维护一个存放令牌的桶,以固定的速率向桶中添加令牌。每次请求到来时,需要从桶中获取一个令牌,只有当桶中有足够的令牌时,请求才能被处理。否则,请求将被拒绝或阻塞。

image

实现思路如下:

  • 维护一个固定大小的令牌桶和一个记录上一次令牌被添加到桶中的时间戳。

  • 以固定的速率(每秒生成的令牌数)向桶中添加令牌。

  • 当请求到来时,尝试从桶中获取一个令牌。如果桶中有令牌,则处理请求并消耗一个令牌;否则,拒绝或阻塞请求。

  • 使用锁或其他同步机制来保证线程安全。

code:

package RateLimiter;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

// 令牌桶算法 (TokenBucketRateLimiter)
public class TokenBucketRateLimiter {
    /**
     * REFILL_PERIOD 表示令牌桶的refill周期,即每隔多长时间(秒)向桶中添加令牌。
     * MAX_TOKENS 表示令牌桶的最大容量,即桶中最多可以存放多少个令牌。
     * REFILL_TOKENS 表示每个refill周期向桶中添加的令牌数量。
     * lastRefillTimestamp 记录上一次refill的时间戳。
     * tokenBucket 是一个Semaphore实例,用于模拟令牌桶的行为。
     */
    private static final long REFILL_PERIOD = 1; // 1秒
    private static final long MAX_TOKENS = 5; // 桶容量
    private static final long REFILL_TOKENS = 2; // 每次添加令牌数
    /**
     * AtomicLong是Java中用于表示一个原子性的长整型值的类。它提供了一些原子操作方法,用于在多线程环境下安全地更新和访问长整型值。
     *  - 在这些限流器实现中,AtomicLong主要用于记录上一次令牌/请求刷新的时间戳。
     *  - 由于多个线程可能同时尝试获取令牌或请求,因此需要确保对时间戳的读写操作是原子性的,以避免竞态条件。
     */
    private AtomicLong lastRefillTimestamp = new AtomicLong(System.nanoTime());
    /**
     * Semaphore(信号量)是Java中一个并发控制工具,用于控制对共享资源的访问。
     *      - 它基于计数器的原理,可以限制同时访问某个资源的线程数量。用于模拟令牌桶的行为。
     *      - Semaphore使用acquire()和release()方法来获取和释放信号量:
     */
    private Semaphore tokenBucket = new Semaphore((int) MAX_TOKENS);

    /**
     * tryAcquire() 方法是获取令牌的入口:
     *
     * 1-获取当前时间戳 now。
     * 2-根据当前时间戳和上一次refill时间戳,计算出这段时间内应该添加多少个令牌 newTokens。
     * 3-更新上一次refill时间戳为当前时间戳。
     * 4-将新的令牌数量 newTokens 释放到 tokenBucket 中。
     * 5-尝试从 tokenBucket 中获取一个令牌,如果成功则返回 true,否则返回 false。
     */
    public boolean tryAcquire() {
        long now = System.nanoTime();
        long lastRefillTime = lastRefillTimestamp.get();
        long newTokens = calculateNewTokens(lastRefillTime, now);
        lastRefillTimestamp.set(now);

        tokenBucket.release((int) newTokens);
        return tokenBucket.tryAcquire();
    }

    /**
     * calculateNewTokens() 方法根据时间差计算出应该添加的令牌数量,但不会超过桶的最大容量。
     */
    private long calculateNewTokens(long lastRefillTime, long now) {
        long nanosElapsed = now - lastRefillTime;
        long refillPeriodCount = nanosElapsed / TimeUnit.SECONDS.toNanos(REFILL_PERIOD);
        return Math.min(refillPeriodCount * REFILL_TOKENS, MAX_TOKENS);
    }
}

漏桶算法(Leaky Bucket)

漏桶算法类似于令牌桶算法,不同之处在于它维护一个存放请求的队列,而不是令牌桶。当请求到来时,它们会被添加到队列中。队列以固定的速率漏水,即以固定的速率处理请求。

image

实现思路如下:

  • 维护一个固定大小的请求队列和一个上次处理请求的时间戳。

  • 当请求到来时,将其添加到队列中。如果队列已满,则拒绝或阻塞请求。

  • 以固定的速率(每秒处理的请求数)从队列中取出请求并处理。

  • 使用锁或其他同步机制来保证线程安全。

code:

package RateLimiter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

// 漏桶算法 (LeakyBucketRateLimiter)
public class LeakyBucketRateLimiter {
    /**
     * REFILL_PERIOD 表示漏桶的refill周期,即每隔多长时间(秒)处理请求。
     * MAX_REQUESTS 表示漏桶的最大容量,即桶中最多可以存放多少个请求。
     * REFILL_REQUESTS 表示每个refill周期处理的请求数量。
     * requestQueue 是一个队列,用于存放待处理的请求。
     * lastRefillTimestamp 记录上一次refill的时间戳。
     */
    private static final long REFILL_PERIOD = 1; // 1秒
    private static final long MAX_REQUESTS = 5; // 桶容量
    private static final long REFILL_REQUESTS = 2; // 每次处理请求数

    private Queue<Long> requestQueue = new LinkedList<>();
    private AtomicLong lastRefillTimestamp = new AtomicLong(System.nanoTime());

    /**
     * tryAcquire() 方法是获取请求的入口:
     *  - 获取当前时间戳 now。
     *  - 根据当前时间戳和上一次refill时间戳,计算出这段时间内应该处理多少个请求 newRequests。
     *  - 更新上一次refill时间戳为当前时间戳。
     *  - 将新的请求数量 newRequests 添加到 requestQueue 中,如果队列已满则移除最早的请求。
     *  - 如果队列大小不超过最大容量 MAX_REQUESTS,则返回 true,否则返回 false。
     */
    public boolean tryAcquire() {
        long now = System.nanoTime();
        long lastRefillTime = lastRefillTimestamp.get();
        long newRequests = calculateNewRequests(lastRefillTime, now);
        lastRefillTimestamp.set(now);

        for (long i = 0; i < newRequests; i++) {
            if (requestQueue.size() >= MAX_REQUESTS) {
                requestQueue.poll();
            }
            requestQueue.offer(now);
        }

        return requestQueue.size() <= MAX_REQUESTS;
    }

    /**
     * calculateNewRequests() 方法根据时间差计算出应该处理的请求数量。
     */
    private long calculateNewRequests(long lastRefillTime, long now) {
        long nanosElapsed = now - lastRefillTime;
        long refillPeriodCount = nanosElapsed / TimeUnit.SECONDS.toNanos(REFILL_PERIOD);
        return refillPeriodCount * REFILL_REQUESTS;
    }
}

滑动窗口(Sliding Window)

滑动窗口算法通过维护一个固定大小的窗口来限制单位时间内的请求数。当请求到来时,它会检查窗口内的请求数是否已达到限制。如果没有,则允许请求;否则,拒绝或阻塞请求。窗口会随着时间推移而滑动,移除较早的请求记录。

冷知识:TCP协议中数据包的传输,同样也是采用滑动窗口来进行流量控制。

实现思路如下:

  • 维护一个队列或其他数据结构来存储请求的时间戳。

  • 当请求到来时,将其时间戳添加到队列中。

  • 检查队列中最近的一段时间内(窗口大小)的请求数是否超过限制。如果没有,则允许请求;否则,拒绝或阻塞请求。

  • 定期(或在每次请求到来时)移除队列中较早的请求记录,以维护窗口大小。

  • 使用锁或其他同步机制来保证线程安全。

Reference:限流之固定窗口/滑动窗口计数法理解-CSDN博客

原理:需要先看看固定窗口算法的原理和缺点,

image

动图:

image

image

image

code:

package RateLimiter;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 滑动窗口算法 (SlidingWindowRateLimiter)
 */
public class SlidingWindowRateLimiter {
    /**
     * WINDOW_SIZE 表示滑动窗口的大小(秒)。
     * MAX_REQUESTS 表示窗口内允许的最大请求数量。
     * requestTimestamps 是一个队列,用于存放请求的时间戳。
     */
    private static final long WINDOW_SIZE = 5; // 窗口大小(秒)
    private static final long MAX_REQUESTS = 10; // 最大请求数
    private Queue<Long> requestTimestamps = new LinkedList<>();

    /**
     * tryAcquire() 方法是获取请求的入口:
     *  - 获取当前时间戳 now。
     *  - 将当前时间戳添加到 requestTimestamps 队列中。
     *  - 计算窗口的开始时间 windowStartTime。
     *  - 移除队列中早于 windowStartTime 的时间戳,即移除窗口之外的请求记录。
     *  - 如果队列大小不超过 MAX_REQUESTS,则返回 true,否则返回 false。
     */
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        requestTimestamps.add(now);

        long windowStartTime = now - WINDOW_SIZE * 1000;
        while (!requestTimestamps.isEmpty() && requestTimestamps.peek() < windowStartTime) {
            requestTimestamps.poll();
        }

        return requestTimestamps.size() <= MAX_REQUESTS;
    }
}

演示code:

package RateLimiter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class RateLimiterDemo {
    public static void main(String[] args) {
        // 创建限流器实例
        TokenBucketRateLimiter tokenBucketRateLimiter = new TokenBucketRateLimiter();
        LeakyBucketRateLimiter leakyBucketRateLimiter = new LeakyBucketRateLimiter();
        SlidingWindowRateLimiter slidingWindowRateLimiter = new SlidingWindowRateLimiter();

        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // 提交任务
        for (int i = 0; i < 20; i++) {
            executorService.submit(() -> {
                boolean tokenBucketAllowed = tokenBucketRateLimiter.tryAcquire();
                boolean leakyBucketAllowed = leakyBucketRateLimiter.tryAcquire();
                boolean slidingWindowAllowed = slidingWindowRateLimiter.tryAcquire();

                System.out.println("Token Bucket: " + tokenBucketAllowed +
                        ", Leaky Bucket: " + leakyBucketAllowed +
                        ", Sliding Window: " + slidingWindowAllowed);

                try {
                    TimeUnit.MILLISECONDS.sleep(500); // 模拟处理请求
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

out:

Token Bucket: true, Leaky Bucket: true, Sliding Window: true
Token Bucket: false, Leaky Bucket: true, Sliding Window: true
Token Bucket: false, Leaky Bucket: true, Sliding Window: true
Token Bucket: false, Leaky Bucket: true, Sliding Window: true
Token Bucket: true, Leaky Bucket: true, Sliding Window: true
Token Bucket: false, Leaky Bucket: true, Sliding Window: true
Token Bucket: false, Leaky Bucket: true, Sliding Window: true
Token Bucket: true, Leaky Bucket: true, Sliding Window: true
Token Bucket: true, Leaky Bucket: true, Sliding Window: true
Token Bucket: true, Leaky Bucket: true, Sliding Window: true
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false
Token Bucket: false, Leaky Bucket: true, Sliding Window: false

总结

这三种算法都是通过控制请求的速率或数量来实现限流,但具体的实现方式有所不同。令牌桶算法和漏桶算法都依赖于时间来控制速率,而滑动窗口算法则是基于请求数量来控制。它们各有优缺点,适合不同的场景。具体选择哪种需要自己根据应用场景进行选择和调整。

同时,也可以考虑使用现有的限流器库或框架,如Guava RateLimiter、Netflix Hystrix等,以简化开发过程。

文章转载自:Mysticbinary

原文链接:https://www.cnblogs.com/mysticbinary/p/18237664

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/699013.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年,计算机相关专业还值得选择吗? 又该如何判断自己是否适合这类专业呢?

文章目录 一、2024年,计算机相关专业还值得选择吗?二、判断自己是否适合这类专业呢&#xff1f;三、哪所大学的计算机专业最好&#xff1f;四、计算机专业是否仍具有长远的发展潜力和就业前景呢? 一、2024年,计算机相关专业还值得选择吗? 在2024年选择大学专业时&#xff0…

开源完全自动化的桌上足球机器人Foosbar;自动编写和修复代码的AI小工具;开源工具,可本地运行,作为Perplexity AI的替代方案

✨ 1: Foosbar Foosbar是一款完全自动化的桌上足球机器人&#xff0c;能与人类玩家对战&#xff0c;具备防守、传球和射门能力。 Foosbar是一个完全自动化的桌上足球机器人&#xff0c;它实现了一侧由机器人控制&#xff0c;另一侧由人类玩家对战的游戏模式。这个机器人能够自…

【论文阅读】Activity Recognition using Cell Phone Accelerometers

Activity Recognition using Cell Phone Accelerometers 引用&#xff1a; Kwapisz J R, Weiss G M, Moore S A. Activity recognition using cell phone accelerometers[J]. ACM SigKDD Explorations Newsletter, 2011, 12(2): 74-82. 论文链接&#xff1a; Activity recogn…

基于JSP的贝儿米幼儿教育管理系统

开头语&#xff1a; 你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果您对本系统感兴趣或者有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; JSP技术 工具&#xff1a; IDEA/Eclipse、…

西南交通大学【操作系统实验7】

实验目的 &#xff08;1&#xff09;理解内存管理中缺页的概念。&#xff08;2&#xff09;综合运用实验1&#xff0c;实验5&#xff0c;实验6中/proc文件系统、内存管理、系统调用、 内核编译的知识。&#xff08;3&#xff09;掌握向/proc文件系统中增加文件的方法。&#…

人人必看:人工智能成熟后,被社会广泛使用后,可能被取代的行业有哪些,以及AI后新兴的行业和职位有哪些?

随着人工智能技术的不断成熟和广泛应用&#xff0c;许多行业和职位可能会受到影响&#xff0c;一些可能被取代&#xff0c;而另一些则会因为AI技术的引入而新兴。人人必看&#xff1a;人工智能成熟后&#xff0c;被社会广泛使用后&#xff0c;可能被取代的行业有哪些&#xff0…

高德地图AI革新:智能导航提升驾驶安全与个性化体验

AITOP100平台了解到&#xff0c;近期&#xff0c;高德地图的用户们在社交平台上分享了令人惊叹的体验&#xff0c;纷纷点赞并称之为“黑科技”。这源于高德地图推出的“车道级安全预警”功能&#xff0c;这一创新不仅适用于两轮和四轮车辆&#xff0c;也成为新老司机的出行必备…

Matlab使用Simulink仿真实现AM和BPSK信号的解调及误码率对比

前言 本篇实现了基于AM和BPSK调制的通信系统&#xff0c;采用Bernoulli Binary Generator生成随机二元序列&#xff0c;码元速率为0.5秒/个。AM调制使用Sine Wave模块生成载波&#xff0c;频率40Hz&#xff0c;相位π/2。BPSK调制通过Switch模块切换相位0和π的载波。信号传输…

乡村振兴的多元化产业发展:推动农村一二三产业融合发展,培育乡村新业态,打造多元化发展的美丽乡村

一、引言 乡村振兴是我国当前及未来一段时间内的重大战略任务&#xff0c;旨在促进农村经济的全面发展&#xff0c;提高农民的生活水平&#xff0c;实现城乡融合发展。在乡村振兴的进程中&#xff0c;推动农村一二三产业融合发展&#xff0c;培育乡村新业态&#xff0c;是打造…

绿色转型,节能攻坚

随着人口增长和经济发展&#xff0c;资源短缺和环境污染问题愈发严重&#xff0c;绿色转型和节能已成为我们共同的责任。为了推动环保事业的发展&#xff0c;阜阳善于善行志愿者团队&#xff0c;参与了本年度以“绿色转型&#xff0c;节能攻坚”为主题的全国节能宣传周活动。这…

echart盒子没有跟着当前div大小变化而自适应

一、问题描述 当echarts图表在一个盒子里的时候&#xff0c;盒子大小变化了&#xff0c;但是图表没有跟着自适应&#xff0c;比如这样&#xff0c;盒子变大了&#xff0c;但是图表没变化 二、解决方法 在盒子大小更改的同时&#xff0c;调用图表的resize方法&#xff0c;记…

海思Hi3519DV500方案1200万无人机吊舱套板

海思Hi3519DV500方案1200万无人机吊舱套板 Hi3519DV500 是一颗面向行业市场推出的超高清智能网络摄像头SoC。该芯片最高 支持四路sensor 输入&#xff0c;支持最高4K30fps 的ISP 图像处理能力&#xff0c;支持2F WDR、 多级降噪、六轴防抖、全景拼接、多光谱融合等多种传统图像…

FL Studio21永久免费破解中文版下载,让我这个音乐制作爱好者如获至宝!

FL Studio21永久免费破解中文版下载&#xff0c;让我这个音乐制作爱好者如获至宝&#xff01;&#x1f3b6; 这款软件功能强大&#xff0c;操作简单易上手。我可以轻松地创作出各种风格的音乐作品。无论是流行、摇滚还是电子音乐&#xff0c;都能轻松驾驭。&#x1f3a7; 使用F…

Java 基础语法

Java 基础语法 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff…

vue3中实现点击ctrl+enter换行,enter发送

效果 TS部分&#xff1a; <script lang"ts" setup> import { Promotion } from element-plus/icons-vue import { ref } from vue;const question ref() const keyDownCode ref(0)// 键盘按下事件处理函数 const keyDownEnter (e: any) > {console.log(…

AI魔法相机:实时3D重建与场景魔法化

一、产品概述 AI魔法相机是一款创新的硬件产品,它结合了AI技术和3D重建扫描技术,能够实时捕捉并重建3D场景和物理世界。用户只需通过简单的点击操作,即可捕捉现实物体或环境,并将其无缝融合到任何场景中,创造出全新的想象现实。 二、核心功能 实时捕捉:一键式操作,迅速…

CV每日论文--2024.6.6

1、Dealing with All-stage Missing Modality: Towards A Universal Model with Robust Reconstruction and Personalization 中文标题&#xff1a;处理全阶段缺失模态&#xff1a;迈向具有鲁棒重建和个性化的通用模型 简介&#xff1a;这篇论文提出了一种具有模态重建和模型个…

QT漂亮QSS样式模仿流行VUE Element UI ,QSS漂亮大方美观样式 QSS样式 QTableWidget 漂亮样式QSS 快速开发QSS漂亮界面

在现代应用程序开发中&#xff0c;用户界面&#xff08;UI&#xff09;的设计与用户体验&#xff08;UX&#xff09;占据了至关重要的位置。Vue.js框架因其灵活性和丰富的生态系统而广受欢迎&#xff0c;其中Element UI作为一套为Vue设计的桌面端组件库&#xff0c;以其清晰的视…

红海云入选《2024中国数据智能产业图谱1.0》

近日&#xff0c;国内知名大数据产业创新服务媒体数据猿携手上海大数据联盟重磅发布了《2024中国数据智能产业图谱1.0》。红海云凭借在人力资源数字化应用领域的卓越产品创新与服务&#xff0c;成功入选图谱「 企业应用-人力资源」板块。 《2024中国数据智能产业图谱1.0》由数…

视频直播点播EasyDSS平台授权时,出现授权时间即将到期的提示是什么原因?

视频直播点播EasyDSS平台具备灵活的视频能力&#xff0c;包括直播、点播、转码、管理、录像、检索、时移回看等&#xff0c;平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等能力服务&#xff0c;可应用在无人机推流、在线直播、虚拟直播、远程培训等场景中。…