常见服务限流方法

news2025/1/6 19:01:30

一、令牌桶算法(Token Bucket)

原理其实很简单,就是设置同一时刻服务器能处理请求的最大数量,如果超过这个数据,则需要等待,或者不处理请求。相当于设置最大并发量,但是细节是,还设置了生成令牌的速率,可以避免后面的请求失败。保证长期请求。具体流程为:

  1. 设置桶的大小,默认是满桶,再设置生成令牌的时间。
  2. 生成的令牌如果在满桶的情况下会溢出
  3. 请求时必须先获取一个令牌,获取后在才可以进行数据请求,会消耗一个桶中的令牌
  4. 如果获取不到令牌则会请求失败,或者等待。
#TokenBucket.py
import time

class TokenBucket:
    def __init__(self, rate, capacity):
        self.rate = rate  
        self.capacity = capacity  
        self.tokens = capacity 
        self.last_time = time.time()  

    def get_token(self)-> bool:
        now = time.time()
        elapsed = int(now - self.last_time)
        self.last_time = now
        self.tokens += elapsed * self.rate
        if self.tokens > self.capacity:
            self.tokens = self.capacity

        if self.tokens >= 1:
            self.tokens -= 1
            return True
        else:
            return False
import tornado.ioloop
import tornado.web
from TokenBucket import TokenBucket

class MainHandler(tornado.web.RequestHandler):
    def initialize(self, bucket):
        self.bucket : TokenBucket = bucket    
    async def get(self):
        if self.bucket.get_token():
            self.write("请求成功")
        else:
            self.set_status(429)
            self.write("请求被拒绝:太多请求")

def make_app():
    bucket = TokenBucket(rate=1, capacity=5)

    return tornado.web.Application([
        (r"/", MainHandler,dict(bucket=bucket)),
    ],debug=True)

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    print("请求地址为 http://localhost:8888")
    tornado.ioloop.IOLoop.current().start()

这里将桶的大小设置为5,速率为一秒钟生成一个令牌,没有令牌就会请求失败,这样可以很好的限制统同一时刻的并发量问题,并且也不会影响后续的请求,因为令牌是不断生成的。

二、漏桶算法(Leaky Bucket)

严格控制请求处理速率,适合需要稳定流量控制的场景。非常类似于令牌桶算法,也是设置一个漏桶的大小,以及流速。将请求比做水滴,每次请求都会进桶,根据流速流出,相当于这个请求完毕。如果桶满的话,需要等待或者不处理。相比于令牌桶的话,请求更加稳定,因为设置的速率,确保系统能够平稳处理流量。例如令牌桶可能会出现一下高并发请求、一下无请求。而漏桶算法就不会,因为速率是恒定的。

  1. 设置桶的大小以及速率
  2. 桶满的情况会溢出,等待或者丢弃
  3. 漏桶会以固定速率处理请求
  4. 直到所有请求完毕
#LeakyBucket.py
import time

class LeakyBucket:
    def __init__(self, capacity, leak_rate):
        self.capacity = capacity 
        self.leak_rate = leak_rate  
        self.tokens = 0  
        self.last_check = time.time() 

    def can_process(self)-> bool:
        current_time = time.time()
        elapsed_time = current_time - self.last_check
        self.last_check = current_time

        processed_tokens = elapsed_time * self.leak_rate
        self.tokens = max(0, self.tokens - processed_tokens)

        if self.tokens < self.capacity:
            self.tokens += 1
            return True
        else:
            return False
import tornado from LeakyBucket import LeakyBucket

class MainHandler(tornado.web.RequestHandler):     
    def initialize(self, bucket):         
        self.bucket : LeakyBucket  = bucket     
        
    async def get(self):         
        if self.bucket.can_process():             
            self.write("请求成功")         
        else:             
            self.set_status(429)             
            self.write("请求失败")         
        self.finish()  
def make_app():     
    bucket = LeakyBucket(capacity=5, leak_rate=1)     
    return tornado.web.Application([         
        (r"/", MainHandler, dict(bucket=bucket)),     
    ],debug = True)  
if __name__ == "__main__":    
app = make_app()     
app.listen(8888)     
print("服务器启动:地址为 http://127.0.0.1:8888")     tornado.ioloop.IOLoop.current().start()

三、对比

为了更好的突出令牌桶算法和漏桶算法的区别,这里使用python写一段脚本进行测试

import requests

for i in range(11):
    response = requests.get("http://localhost:8888")
    print(response.status_code, response.text)

令牌桶算法
(https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp0-xtjj-private.juejin.cn%2Ftos-cn-i-73owjymdk6%2F03eda7c2339c4390a3dd5697b614ffda~tplv-73owjymdk6-jj-mark%3A0%3A0%3A0%3A0%3Aq75.awebp%3Fpolicy%3DeyJ2bSI6MywidWlkIjoiMjY2MzQ0MzkyNTA0NDkwNiJ9%26rk3s%3De9ecf3d6%26x-orig-authkey%3Df32326d3454f2ac7e96d3d06cdbb035152127018%26x-orig-expires%3D1722495703%26x-orig-sign%3Dcx2DDRnhspueuyBpaVlw9uP3Huw%253D&pos_id=img-0TAYnV9g-1722410114551)
漏桶算法

(https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp0-xtjj-private.juejin.cn%2Ftos-cn-i-73owjymdk6%2F56ce61cf2753400cb40bf02009efe48c~tplv-73owjymdk6-jj-mark%3A0%3A0%3A0%3A0%3Aq75.awebp%3Fpolicy%3DeyJ2bSI6MywidWlkIjoiMjY2MzQ0MzkyNTA0NDkwNiJ9%26rk3s%3De9ecf3d6%26x-orig-authkey%3Df32326d3454f2ac7e96d3d06cdbb035152127018%26x-orig-expires%3D1722495703%26x-orig-sign%3DY5ZgC9KA5SoOIIWcAcoM1%252FgbWmo%253D&pos_id=img-12oNCfO3-1722410068178)

这样就可以很直观的感受变化。桶令牌算法可能会出现很大的波动,而漏桶算法在解决大量请求时,处理的方式就特别稳定。具体的速率需要根据实际情况。

四、固定窗口计数器(Fixed Window Counter)

简单来说就是在指定时间的内可以允许的请求数量,也就是将这个时间段比作窗口。这个窗口在一定时间内只能处理指定的请求数据,超过的请求等待或者不处理。通过不断重置起始时间来保证窗口。

  1. 设置窗口大小以及最大请求数量
  2. 请求时检查时间是否超过,再检查请求数量是否超过
  3. 超过则拒绝请求。
#FixedWindowCounter.py
import time

class FixedWindowCounter:
    def __init__(self, limit, window_size):
        self.limit = limit 
        self.window_size = window_size
        self.request_count = 0 
        self.window_start = time.time()

    def can_process(self) -> bool:
        current_time = time.time()
        if current_time - self.window_start >= self.window_size:
            self.window_start = current_time
            self.request_count = 0
            
        if self.request_count < self.limit:
            self.request_count += 1
            return True
        else:
            return False
import tornado
from FixedWindowCounter import FixedWindowCounter

class MainHandler(tornado.web.RequestHandler):
    def initialize(self, counter):
        self.counter : FixedWindow = counter

    async def get(self):
        if self.counter.can_process():
            self.write("请求成功")
        else:
            self.set_status(429)  
            self.write("请求被拒绝")

def make_app():
    counter = FixedWindowCounter(limit=100, window_size=60)  
    return tornado.web.Application([
        (r"/", MainHandler, dict(counter=counter)),
    ], debug=True)

if __name__ == "__main__":
    app = make_app()
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8888)
    print("服务器正在监听端口 http://localhost:8888")
    tornado.ioloop.IOLoop.current().start()

这里将窗口的的大小设置为一分钟,最多允许100次请求。一分钟内超过的请求不处理。这样的劣势是太过集中,并且存在边界问题,也就是在窗口马上结束,下一个窗口马上开始时突然大量请求。

五、滑动窗口计数器(Sliding Window Counter)

相比于固定窗口计数器多了一步清除旧的请求。在每次请求时会清除旧的请求。通过动态调整窗口的起点和终点,确保在任意连续时间段内最多处理设定的请求数。当一个新请求到达时,滑动窗口会移除当前窗口外的过期请求,然后检查当前窗口内的请求数是否超过限制。更精确地控制请求流量,减少边界突发流量问题。

  1. 设置窗口大小以及最大请求量
  2. 检查请求,过滤掉当前时间窗口外的过期请求
  3. 判断窗口容量,添加请求的时间戳
  4. 否则不予请求
#SlidingWindowCounter.py
import time

class SlidingWindowCounter:
    def __init__(self, limit, window_size):
        self.limit = limit  
        self.window_size = window_size  
        self.requests = [] 

    def can_process(self) -> bool:
        current_time = time.time()
        self.requests = [req 
                            for req in self.requests 
                            if req > current_time - self.window_size]
        if len(self.requests) < self.limit:
            self.requests.append(current_time)
            print("请求成功")
            return True
        else:
            return False
import tornado
from SlidingWindowCounter import SlidingWindowCounter

class MainHandler(tornado.web.RequestHandler):
    def initialize(self, counter):
        self.counter : SlidingWindowCounter = counter

    async def get(self):
        if self.counter.can_process():
            self.write("请求成功")
        else:
            self.set_status(429)  
            self.write("请求被拒绝")

def make_app():
    counter = SlidingWindowCounter(limit=10, window_size=10)  
    return tornado.web.Application([
        (r"/", MainHandler, dict(counter=counter)),
    ], debug=True)

if __name__ == "__main__":
    app = make_app()
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8888)
    print("服务器正在监听端口 http://localhost:8888")
    tornado.ioloop.IOLoop.current().start()
   

六、请求队列(Request Queue)

依赖于队列的先进先出的性质,将请求放入队列中,按顺序处理,超出的请求拒绝或者丢弃。达到限流的目的。


#FixedWindowQueueCounter.py
from collections import deque

class FixedWindowQueueCounter:
    def __init__(self, limit):
        self.limit = limit
        self.queue = deque()  

    def can_process(self) -> bool:
        if len(self.queue) < self.limit:
            self.queue.append(1) 
            return True
        else:
            return False

    def process_request(self):
        if self.queue:
            self.queue.popleft()  
import tornado

from FixedWindowQueueCounter import FixedWindowQueueCounter

class MainHandler(tornado.web.RequestHandler):
    def initialize(self, counter):
        self.counter: FixedWindowQueueCounter = counter

    async def get(self):
        if self.counter.can_process():
            self.write("请求成功")
            self.counter.process_request()
        else:
            self.set_status(429)  
            self.write("请求被拒绝")

def make_app():
    counter = FixedWindowQueueCounter(limit=10) 
    return tornado.web.Application([
        (r"/", MainHandler, dict(counter=counter)),
    ], debug=True)

if __name__ == "__main__":
    app = make_app()
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8888)
    print("服务器正在监听端口 http://localhost:8888")
    tornado.ioloop.IOLoop.current().start()

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

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

相关文章

解决nginx端口转发后,获取不到真实IP问题

文章目录 1&#xff0c;设置nginx端口转发1.2&#xff0c;无法获取客户端真实IP 2&#xff0c;nginx配置文件增加配置&#xff0c;保留客户端信息2.2&#xff0c;可以看到真实IP信息 1&#xff0c;设置nginx端口转发 location /AWAPI/ {proxy_pass http://172.28.43.19:9607; …

组件化开发

1.组件化开发 组件化&#xff1a;一个页面可以拆分成一个个组件&#xff0c;每个组件有着自己独立的结构[html]、样式[css]、行为 [js]。好处&#xff1a;便于维护&#xff0c;利于复用 → 提升开发效率。组件分类&#xff1a;普通组件、根组件。比如&#xff1a;下面这个页面…

二级MySQL(十二)——分组聚合查询

首先整理常用的聚合函数&#xff1a; 函数名说明COUNT&#xff08;*&#xff09;记录数COUNT&#xff08;列名&#xff09;一列的记录数MAX&#xff08;列名&#xff09;一列的最大值 MIN&#xff08;列名&#xff09; 一列的最小值 SUM&#xff08;列名&#xff09;一列…

M12电连接器航插插座L-code

M12电连接器概述 M12电连接器是一种广泛应用于工业自动化、传感器、仪器仪表、数据通信和控制系统等领域的圆形连接器。它的核心特点在于其小巧的尺寸、强大的多信号传输能力和出色的防水性能&#xff0c;使其成为众多工业应用的首选。M12连接器通常具有3至12个引脚&#xff0…

redis主从复制、哨兵模式、集群

redis集群 高可用 redis集群的三种模式&#xff1a; 1.主从复制&#xff08;奇书 3台 一主两从&#xff09; 2.哨兵模式 &#xff08;3台 一主两从&#xff09; 3.cluster &#xff08;集群 6 333&#xff09; 主从复制&#xff1a;喝MySQL的主从复制类似&#xff0c;主可以写…

vite创建Vue2项目(配图详细)

参考文章&#xff1a;vite项目生成vue3并引入element-ui vite脚手架生成vue项目及其配置_viteconfig配置-CSDN博客 Vite 默认支持 Vue 3&#xff0c;但你也可以使用 Vite 来搭建 Vue 2 的项目。不过&#xff0c;这需要一些额外的配置&#xff0c;因为 Vue 2 不支持原生的 ES …

【电子通识】什么是SIM卡/eSIM?

什么是SIM卡。 1991年&#xff0c;世界第一张SIM卡被德国捷德公司开发&#xff0c;当时的SIM卡非常大&#xff0c;和银行IC卡一样&#xff1a; SIM卡的全名是“用户识别模块”&#xff08;Subscriber Identity Module&#xff09;&#xff1a;这块镀金的电路芯片拥有身份识别功…

请大家监督:我要开启Python之路,首要任务最简单的搭建环境

任务说明&#xff1a; 如上图所示&#xff0c;Python稳稳第一&#xff0c;为何&#xff1f;因为Python可以做很多事情&#xff0c;比如&#xff1a;Web开发&#xff0c;网络爬虫&#xff0c;软件开发、数据分析、游戏开发&#xff0c;金融分析&#xff0c;人工智能与机器学习&a…

Java每日面试题(事务相关)(day5)

目录 什么是事务&#xff1f;spring事务的实现方式事务失效的8种情况 什么是事务&#xff1f; 事务是一个操作序列&#xff0c;要么全部执行成功&#xff0c;要么全部执行失败。事务有四个重要特性&#xff0c;称为 ACID 特性&#xff1a; Atomicity&#xff08;原子性&#x…

从教学到分享,2024精选录屏工具

如果你在公司里承担会议记录的职责&#xff0c;那录屏这项技能你一定要学会。像录屏大师这样的工具可以帮你在远程会议中进行录屏操作&#xff0c;方便你后期整理会议内容。 1.福昕录屏大师 链接直达&#xff1a;https://www.foxitsoftware.cn/REC/ 这款录屏工具提供了多种…

自定义线程池(二)

上节回顾 在上一节当中&#xff0c;已经实现了一个线程池&#xff0c;在本节当中&#xff0c;我们需要添加拒绝策略。这里使用到了策略模式的设计模式&#xff0c;因为拒绝策略是多种的&#xff0c;我们需要将这个权利下放给调用者&#xff08;由调用者来指定我要采取哪种策略…

代码随想录算法训练营第41天|LeetCode 121. 买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III

1. LeetCode 121. 买卖股票的最佳时机 题目链接&#xff1a;https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/ 文章链接&#xff1a;https://programmercarl.com/0121.买卖股票的最佳时机.html#思路 视频链接&#xff1a;https://www.bilibili.com/…

微调基模型

该示例用的谷歌的gemma-2b-it模型 Gemma是Google的一系列轻量级、最先进的开源模型&#xff0c;基于用于创建Gemini模型的相同研究和技术构建。它们是文本到文本、仅解码的大型语言模型&#xff0c;提供英文、开源权重、预训练变体和指令调优变体。Gemma模型非常适合各种文本生…

微软蓝屏事件:网络安全与系统稳定性的深刻反思

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 写在前面 在数字化时代&#xff0c;软件更新已成为…

电测量数据交换DLMS∕COSEM组件第61部分:对象标识系统(OBIS)(下)

GB/T 17215.6的本部分规定了对象标识系统(OBIS)的总体结构并将测量设备中的所有常用数据项映射到其标识代码。OBIS为测量设备中的所有数据都提供唯一的标识符,不仅包括测量值,而且还包括仪表设备的配置或获取测量设备运行状态的抽象数据。 5.抽象对象(A=0) 5.1通用和服…

yolov10来了!用yolov10训练自己的数据集(原理、训练、部署、应用)

一、引言 YOLOv9还没热乎呢&#xff0c;YOLOv10就出来了&#xff0c;太卷了&#xff01;太快了&#xff01; 自今年2月YOLOv9发布之后&#xff0c; YOLO&#xff08;You Only Look Once&#xff09; 系列的接力棒传到了清华大学研究人员的手上。YOLOv10推出的消息引发了AI界的…

【第七天】TCP三次握手四次挥手 HTTP的keep-Alive和TCP的keepalive

TCP三次握手四次挥手 既然要了解这些&#xff0c;首先我们要清楚一个概念&#xff0c;半连接队列和全连接队列&#xff1a; 在TCP三次握手中&#xff0c;Linux内核会维护两个队列来管理连接请求。 这两个队列的存在是为了处理并发连接请求&#xff0c;确保服务端能够有效管理新…

设计模式17-适配模式

设计模式17-适配模式 动机定义与结构C代码推导总结应用具体应用示例 动机 在软件系统中由于应用环境的变化常常需要将一些现存的对象。放到新的环境中去应用。但是新环境要求的接口是这些现存对象所不满足的。那么这种情况下如何应对这种迁移的变化&#xff1f;如何既能利用现…

《零散知识点 · SpringBoot 整合邮件功能》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

光伏气象仿真系统主要功能点

光伏电站的效能与经济效益受气象条件、地理位置、设计布局等多种因素影响显著。因此&#xff0c;光伏气象仿真系统应运而生&#xff0c;成为提升光伏项目规划、设计、运营管理水平的重要工具。该系统集成了气象数据分析、发电量分析、投融资分析、损耗估算分析及光伏设计等多项…