【算法】令牌桶算法

news2024/9/22 21:20:12

一、引言

        令牌桶算法(Token Bucket Algorithm, TBA)是一种流行于网络通信领域的流量控制和速率限制算法。它允许一定程度的突发传输,同时限制长时间内的传输速率。令牌桶算法广泛应用于网络流量管理、API请求限流等场景。其基本原理是通过一个“桶”来控制数据的发送速率。桶内存储一定数量的“令牌”,每个令牌代表一个数据包的发送权限。令牌以固定的速率生成,数据包的发送需要消耗令牌。若桶内没有令牌,数据包则需要等待,直到有令牌可用。

二、算法原理

令牌桶算法基于以下核心概念:

  • 令牌桶:一个虚拟的容器,用来存放固定数量的令牌。
  • 令牌填充速率:系统以固定的速率向桶中添加令牌。
  • 令牌消耗:每当一个数据包发送时,就从桶中移除一个k如果桶中没有令牌,数据包将被延迟发送或丢弃,直到桶中有足够的令牌。

三、数据结构

令牌桶算法需要以下数据结构:

  • 令牌桶:存储令牌的容器。
  • 时间戳:记录上一次填充令牌的时间。

四、算法使用场景

令牌桶算法适用于:

  • 网络带宽管理:控制用户的网络流量,防止滥用。
  • API速率限制:限制API调用频率,保护后端服务。
  • 云服务提供商:为不同级别的用户提供不同速率的服务。
  • 任务调度:限制任务执行的速率,避免资源争用。

五、算法实现

初始化:设置令牌桶的容量和生成令牌的速率。

生成令牌:以固定的时间间隔向桶中添加令牌,直到达到桶的最大容量。

请求处理:检查桶中是否有令牌。如果有,消耗一个令牌并处理请求。如果没有,根据策略拒绝请求或等待。

伪代码:

function acquireToken(tokensRequested):
currentTime = getCurrentTime()
elapsedTime = currentTime - lastRefillTimestamp
newTokens = elapsedTime * refillRate
tokens = min(capacity, tokens + newTokens)
lastRefillTimestamp = currentTime

if tokens >= tokensRequested:
tokens -= tokensRequested
return true
else:
return false

六、其他同类算法对比

漏桶算法 (Leaky Bucket):漏桶算法具有恒定的输出速率,处理数据的速度不会因为突发流量而变化。适合于需要恒定速率输出的场景。

固定窗口计数算法 (Fixed Window Counter):统计在固定时间窗口内的请求数量,适合简单的请求限制,但对突发流量不够友好。

滑动窗口计数算法 (Sliding Window Counter):在固定时间窗口中,使用滑动窗口来统计请求数量,能够更平滑地控制流量。

计数器算法(Counter Algorithm):计数器算法通过计数器来限制一段时间内的请求次数,适用于简单的速率限制场景。

七、多语言实现

Java

import java.util.concurrent.TimeUnit;

public class TokenBucket {
    private final long capacity;
    private long tokens;
    private final long refillRate;
    private long lastRefillTime;

    public TokenBucket(long capacity, long refillRate) {
        this.capacity = capacity;
        this.tokens = capacity;
        this.refillRate = refillRate;
        this.lastRefillTime = System.currentTimeMillis();
    }

    private void refill() {
        long now = System.currentTimeMillis();
        long elapsed = now - lastRefillTime;
        long newTokens = elapsed * refillRate / 1000;
        tokens = Math.min(capacity, tokens + newTokens);
        lastRefillTime = now;
    }

    public synchronized boolean consume(long tokensToConsume) {
        refill();
        if (tokens >= tokensToConsume) {
            tokens -= tokensToConsume;
            return true;
        }
        return false;
    }
}

Python

import time
from threading import Lock

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        self.capacity = capacity
        self.tokens = capacity
        self.refill_rate = refill_rate
        self.last_refill_time = time.time()
        self.lock = Lock()

    def refill(self):
        current_time = time.time()
        elapsed = current_time - self.last_refill_time
        new_tokens = elapsed * self.refill_rate
        self.tokens = min(self.capacity, self.tokens + new_tokens)
        self.last_refill_time = current_time

    def consume(self, tokens_to_consume):
        with self.lock:
            self.refill()
            if self.tokens >= tokens_to_consume:
                self.tokens -= tokens_to_consume
                return True
            return False

C++

#include <chrono>
#include <mutex>

class TokenBucket {
public:
    TokenBucket(long capacity, long refillRate)
        : capacity(capacity), tokens(capacity), refillRate(refillRate),
          lastRefillTime(std::chrono::steady_clock::now()) {}

    bool consume(long tokensToConsume) {
        std::lock_guard<std::mutex> lock(mtx);
        refill();
        if (tokens >= tokensToConsume) {
            tokens -= tokensToConsume;
            return true;
        }
        return false;
    }

private:
    long capacity;
    long tokens;
    long refillRate;
    std::chrono::steady_clock::time_point lastRefillTime;
    std::mutex mtx;

    void refill() {
        auto now = std::chrono::steady_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - lastRefillTime).count();
        long newTokens = elapsed * refillRate;
        tokens = std::min(capacity, tokens + newTokens);
        lastRefillTime = now;
    }
};

Go

package main

import (
	"sync"
	"time"
)

type TokenBucket struct {
	capacity       int64
	tokens         int64
	refillRate     int64
	lastRefillTime time.Time
	mu             sync.Mutex
}

func NewTokenBucket(capacity int64, refillRate int64) *TokenBucket {
	return &TokenBucket{
		capacity:       capacity,
		tokens:         capacity,
		refillRate:     refillRate,
		lastRefillTime: time.Now(),
	}
}

func (tb *TokenBucket) refill() {
	now := time.Now()
	elapsed := now.Sub(tb.lastRefillTime).Seconds()
	newTokens := int64(elapsed) * tb.refillRate
	tb.tokens = min(tb.capacity, tb.tokens+newTokens)
	tb.lastRefillTime = now
}

func (tb *TokenBucket) Consume(tokensToConsume int64) bool {
	tb.mu.Lock()
	defer tb.mu.Unlock()
	tb.refill()
	if tb.tokens >= tokensToConsume {
		tb.tokens -= tokensToConsume
		return true
	}
	return false
}

func min(a, b int64) int64 {
	if a < b {
		return a
	}
	return b
}

八. 实际的服务应用场景代码框架

场景描述

        假设我们正在开发一个API服务,需要限制每个用户的请求频率,以防止滥用。我们可以使用令牌桶算法来实现这一功能。

代码框架

        简单的Java Spring Boot应用程序的代码框架,展示如何使用令牌桶算法进行API请求限流。

项目结构
src
└── main
    ├── java
    │   └── com
    │       └── example
    │           ├── TokenBucket.java
    │           └── ApiController.java
    └── resources
        └── application.properties
算法逻辑

TokenBucket.java

import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class TokenBucket {
    private final long capacity = 10; // 最大令牌数
    private long tokens = capacity;
    private final long refillRate = 1; // 每秒生成1个令牌
    private long lastRefillTime = System.currentTimeMillis();

    private synchronized void refill() {
        long now = System.currentTimeMillis();
        long elapsed = now - lastRefillTime;
        long newTokens = TimeUnit.MILLISECONDS.toSeconds(elapsed) * refillRate;
        tokens = Math.min(capacity, tokens + newTokens);
        lastRefillTime = now;
    }

    public synchronized boolean consume(long tokensToConsume) {
        refill();
        if (tokens >= tokensToConsume) {
            tokens -= tokensToConsume;
            return true;
        }
        return false;
    }
}
控制器

ApiController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiController {

    @Autowired
    private TokenBucket tokenBucket;

    @GetMapping("/api/resource")
    public String getResource(@RequestParam String userId) {
        if (tokenBucket.consume(1)) {
            return "Resource accessed successfully.";
        } else {
            return "Too many requests. Please try again later.";
        }
    }
}
配置

application.properties

server.port=8080
启动服务

在项目根目录下,使用以下命令启动Spring Boot应用:

./mvnw spring-boot:run
测试

使用Postman或cURL测试API:

curl "http://localhost:8080/api/resource?userId=123"

        令牌桶算法是一种有效的流量控制技术,能够平滑流量并限制突发请求。通过在桶中动态生成和管理令牌来限制数据发送速率。算法的核心原理是设置桶的容量和令牌生成速率,从而控制请求处理的速率,适用于网络流量控制和API限流等场景。相比其他算法(如漏桶算法、固定窗口计数等),令牌桶能更灵活地应对突发流量。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3 响应式 API:computed()

介绍 基本概念&#xff1a; computed()接收一个 getter 函数或者一个包含 getter 和 setter 函数的对象作为参数&#xff0c;并返回一个基于原始响应式数据计算得到的新的响应式数据对象。计算属性的值会根据其依赖的响应式数据自动更新&#xff0c;当依赖的数据发生变化时&am…

设计模式23-职责链

设计模式23-职责链 动机定义与结构定义结构职责链模式图中元素解释工作流程 C 代码推导优缺点应用场景总结 动机 在软件构建过程中&#xff0c;一个请求可能被多个对象处理。但是每个请求在运行时只能有一个接受者。如果显示指定将必不可少的带来请求发送者与接受者的紧耦合。…

lvs+keepalive大战haproxy+keepalive实现高可用集群

华子目录 lvskeepalive实验架构实验前的准备工作1.主机准备2.KA1和KA2上安装lvskeepalive3.webserver1和webserver2上安装httpd4.制作测试效果网页内容5.所有主机关闭firewalld和selinux6.开启httpd服务 实验步骤1.webserver1和webserver2上配置vip2.webserver1和webserver2上关…

【C语言】 作用域和存储期

C语言的作用域和存储期 一、作用域1、概念&#xff1a;2、函数声明作用域3、局部作用域4、全局作用域5、作用域的临时掩盖6、static关键字 二、存储期1、概念2、自动存储期3、静态存储期4、自定义存储期 一、作用域 1、概念&#xff1a; \quad C语言中&#xff0c;标识符都有一…

《小迪安全》学习笔记03

须知少时凌云志&#xff0c;曾许人间第一流。 静态页面&#xff08;HTML&#xff09;是没有漏洞的&#xff0c;没有数据传递。 动态网站一般来说&#xff0c;有开发组合&#xff0c;即开发语言和数据库&#xff0c;两者兼容性比较好&#xff1a; 而且有的数据库不支持windows或…

网站自动化锚文本的实现逻辑

锚文本&#xff0c;‌即超链接的文本部分&#xff0c;‌它在网页中扮演着至关重要的角色。‌通过点击锚文本&#xff0c;‌用户可以方便地在网页间进行跳转&#xff0c;‌从而极大地提升了用户体验。‌同时&#xff0c;‌在搜索引擎优化&#xff08;‌SEO&#xff09;‌领域&am…

idea 修改背景图片教程

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明修改背景图片 开篇说明 给小白看得懂的修改图片教程&…

Spring项目:文字花园(三)

一.实现博客详情 1.后端逻辑代码 controller层添加方法&#xff08;根据博客id查看博客详情&#xff09; RequestMapping("/getBlogDetail")public Result<BlogInfo> getBlogDetail(Integer blogId){log.info("getBlogDetail, blogId: {}", blogId…

并发编程 | Future是如何优化程序性能

在初识Future一文中介绍了Future的核心方法。本文中接着介绍如何用Future优化我们的程序性能。 在此之前&#xff0c;有必要介绍Future接口的一个实现类FutureTask。 FutureTask介绍 FutureTask继承结构 首先我们看一下FutureTask的继承结构&#xff1a; public class Futur…

JUC阻塞队列(二):LinkedBlockingQueue

1、LinkedBlockingQueue 介绍 LinkedBlockingQueue 也是接口BlockingQueue的一个实现类&#xff0c;与 ArrrayBlockingQueue基于 数组实现不同的是&#xff0c;LinkedBlockingQueue是基于单项链表实现的&#xff0c;在LinkedBlockingQueue 内部维护了一个单向链表来存储数据&am…

探索深度学习的力量:从人工智能到计算机视觉的未来科技革命

目录 1. 引言 2. 人工智能的历史背景 3. 深度学习的崛起 3.1 深度神经网络的基本原理 4. 计算机视觉的发展现状 4.1 传统计算机视觉与深度学习的结合 5. 深度学习在计算机视觉中的应用 5.1 图像分类 5.2 目标检测 6. 深度学习引领的未来科技创新与变革 7. 结论 引言…

opencv cv.findContours 函数图像轮廓层级(记录)

opencv cv.findContours 函数详解 图像轮廓层级 图像轮廓检索方式 cv.findContours contours, hierarchy cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] ) 参数1&#xff1a;源图像 参数2&#xff1a;轮廓的检索方式&#xff0c;主要参数 参数3…

专题--自底向上的计算机网络(物理层)

目录 计算机网络概述 物理层 数据链路层 网络层 运输层 应用层 网络安全 详细见http://t.csdnimg.cn/MY5aI http://t.csdnimg.cn/8Ipa4 http://t.csdnimg.cn/uvMxS

康耐视相机与发那科机器人通过Ethernet I/P直连与程序编写

配置TCP/IP&#xff1a;按MENU—SETUP—NEXT—HOSTCOMM&#xff0c;选择TCP/IP—按ENTER或者F3[DETAIL] Port#1 IP addr&#xff1a;输入机器人IP地址&#xff0c;按ENTER后输入&#xff0c;如192.168.1.11&#xff1b;如果控制柜有2个网络端口&#xff0c;则按F3[PORT]进行切换…

科创中心“核”动力|趋动科技:AI算力界的领跑者

近日&#xff0c;趋动科技与深信服正式推出联合解决方案。联合解决方案将深信服EDS的高性能存储与趋动科技OrionX AI算力资源池化软件、以及GeminiAI训练平台有机结合&#xff0c;整合存力与算力资源的同时&#xff0c;帮助用户建好AI平台、管好AI资源、用好AI服务。 双方已完成…

监控zabbix的安装与使用

文章目录 1.zabbix的安装步骤2.zabbix的主动模式和被动模式简介及实现3.zabbix proxy主动及被动4.自定义监控&#xff0c;监控linux和连接状态&#xff0c;创建email进行基础报警5.部署zabbix agent脚本&#xff0c;适配rocky和ubuntu系统6.使用脚本&#xff0c;基于zabbix api…

yolov8旋转框+关键点检测

一、Yolov8obb_kpt -----------------------------------现已在v8官方库上更新旋转框分割算法和旋转框关键点检测算法-------------------------- ------------------------------------------- https://github.com/yzqxy/ultralytics-obb_segment---------------------------…

每天五分钟深度学习框架pytorch:自动求导机制

本文重点 深度学习框架pytorch拥有自动求导的机制,自动求导是 PyTorch 中非常重要的特性,能够让我们避免手动去计算非常复杂的导数,这能够极大地减少了我们构建模型的时间。本文学习的是第10步反向传播,学习路线参考前面一篇文章。 pytorch0.4版本 在pytorch的0.4版本中…

YOLO知识点总结:

分类&#xff1a; 即是将图像结构化为某一类别的信息&#xff0c;用事先确定好的类别(category)或实例ID来描述图片。这一任务是最简单、最基础的图像理解任务&#xff0c;也是深度学习模型最先取得突破和实现大规模应用的任务。其中&#xff0c;ImageNet是最权威的评测集&…

C语言刷题日记(附详解)(1)

一、选择判断部分 第一题&#xff1a; 如下代码是否存在风险&#xff0c;并说明原因和修改方案 #include<stdio.h> int main() {char* str "hello world";*str a;return 0; }思路提示&#xff1a;这种形式的字符串存储在什么区域呢&#xff1f;是否真的有…