最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

從原理到生產(chǎn)落地詳解Python高并發(fā)服務(wù)限流的終極方案

 更新時(shí)間:2026年02月28日 09:20:43   作者:哈里謝頓  
在高并發(fā)場(chǎng)景下,無(wú)限制的請(qǐng)求會(huì)導(dǎo)致服務(wù)雪崩,數(shù)據(jù)庫(kù)擊穿,資源耗盡和安全風(fēng)險(xiǎn),下面小編將和大家詳細(xì)介紹一下Python高并發(fā)服務(wù)限流的終極方案,希望對(duì)大家有所幫助

適用讀者:Python 后端工程師、SRE、API 網(wǎng)關(guān)開(kāi)發(fā)者

技術(shù)棧:FastAPI / Flask / Django + Redis + asyncio

場(chǎng)景:Web API、微服務(wù)、爬蟲(chóng)防護(hù)、支付系統(tǒng)

更新日期:2026 年 2 月

一、為什么需要限流

在高并發(fā)場(chǎng)景下,無(wú)限制的請(qǐng)求會(huì)導(dǎo)致:

  • 服務(wù)雪崩:CPU/內(nèi)存打滿,響應(yīng)超時(shí)
  • 數(shù)據(jù)庫(kù)擊穿:大量請(qǐng)求穿透緩存壓垮 DB
  • 資源耗盡:第三方 API 調(diào)用超配額(如短信、支付)

限流的核心目標(biāo)“在保證服務(wù)質(zhì)量的前提下,優(yōu)雅拒絕超額請(qǐng)求。”

二、限流算法詳解(附 Python 實(shí)現(xiàn))

1.固定窗口(Fixed Window)— 最簡(jiǎn)單但有缺陷

原理

  • 將時(shí)間劃分為固定窗口(如 1 分鐘)
  • 每個(gè)窗口內(nèi)最多允許 N 次請(qǐng)求
  • 窗口切換時(shí)計(jì)數(shù)重置

Python 實(shí)現(xiàn)(Redis 版)

import redis
import time

class FixedWindowRateLimiter:
    def __init__(self, redis_client, key_prefix, limit, window=60):
        self.redis = redis_client
        self.key_prefix = key_prefix
        self.limit = limit
        self.window = window  # 秒

    def is_allowed(self, key: str) -> bool:
        full_key = f"{self.key_prefix}:{key}"
        current = int(time.time())
        window_start = (current // self.window) * self.window
        
        # 使用 Redis pipeline 保證原子性
        pipe = self.redis.pipeline()
        pipe.zremrangebyscore(full_key, 0, window_start - 1)
        pipe.zcard(full_key)
        pipe.zadd(full_key, {str(current): current})
        pipe.expire(full_key, self.window + 1)
        _, count, _, _ = pipe.execute()
        
        return count < self.limit

缺陷

臨界問(wèn)題:在窗口切換瞬間可能接受 2×limit 請(qǐng)求

(如 00:59 發(fā) 100 次,01:00 又發(fā) 100 次)

2.滑動(dòng)窗口(Sliding Window)— 更平滑

原理

  • 記錄每個(gè)請(qǐng)求的時(shí)間戳
  • 每次請(qǐng)求時(shí),清理 當(dāng)前時(shí)間 - 窗口 之前的記錄
  • 統(tǒng)計(jì)剩余請(qǐng)求數(shù)是否超限

Python 實(shí)現(xiàn)(Redis ZSET)

class SlidingWindowRateLimiter:
    def __init__(self, redis_client, key_prefix, limit, window=60):
        self.redis = redis_client
        self.key_prefix = key_prefix
        self.limit = limit
        self.window = window

    def is_allowed(self, key: str) -> bool:
        full_key = f"{self.key_prefix}:{key}"
        now = time.time()
        window_start = now - self.window
        
        pipe = self.redis.pipeline()
        # 移除窗口外的請(qǐng)求
        pipe.zremrangebyscore(full_key, 0, window_start)
        # 獲取當(dāng)前窗口內(nèi)請(qǐng)求數(shù)
        pipe.zcard(full_key)
        # 添加當(dāng)前請(qǐng)求
        pipe.zadd(full_key, {str(now): now})
        # 設(shè)置過(guò)期時(shí)間(避免冷 key 占用內(nèi)存)
        pipe.expire(full_key, int(self.window) + 1)
        
        _, count, _, _ = pipe.execute()
        return count <= self.limit

優(yōu)點(diǎn)

  • 解決了固定窗口的臨界問(wèn)題
  • 精確控制任意時(shí)間窗口內(nèi)的請(qǐng)求量

缺點(diǎn)

  • 內(nèi)存占用高(需存儲(chǔ)所有時(shí)間戳)
  • 高頻請(qǐng)求下 Redis ZSET 操作開(kāi)銷(xiāo)大

3.令牌桶(Token Bucket)— 推薦生產(chǎn)使用

原理

  • 桶容量 = burst(突發(fā)流量)
  • 令牌生成速率 = rate(如 100 token/秒)
  • 請(qǐng)求到來(lái)時(shí)嘗試獲取令牌,失敗則拒絕

Python 實(shí)現(xiàn)(Redis Lua 腳本保證原子性)

import json

class TokenBucketRateLimiter:
    LUA_SCRIPT = """
    local tokens_key = KEYS[1]
    local timestamp_key = KEYS[2]
    local rate = tonumber(ARGV[1])
    local capacity = tonumber(ARGV[2])
    local now = tonumber(ARGV[3])
    local requested = tonumber(ARGV[4])

    local last_tokens = redis.call('GET', tokens_key)
    if not last_tokens then
        last_tokens = capacity
    end

    local last_time = redis.call('GET', timestamp_key)
    if not last_time then
        last_time = now
    end

    local tokens = tonumber(last_tokens)
    local last_time = tonumber(last_time)

    -- 計(jì)算新令牌數(shù)
    local new_tokens = tokens + (now - last_time) * rate
    if new_tokens > capacity then
        new_tokens = capacity
    end

    local allowed = new_tokens >= requested
    if allowed then
        new_tokens = new_tokens - requested
    end

    -- 更新?tīng)顟B(tài)
    redis.call('SET', tokens_key, new_tokens)
    redis.call('SET', timestamp_key, now)
    redis.call('EXPIRE', tokens_key, 10)
    redis.call('EXPIRE', timestamp_key, 10)

    return {allowed and 1 or 0, new_tokens}
    """

    def __init__(self, redis_client, key_prefix, rate, capacity):
        self.redis = redis_client
        self.key_prefix = key_prefix
        self.rate = rate      # 令牌生成速率(token/秒)
        self.capacity = capacity  # 桶容量
        self.script = self.redis.register_script(self.LUA_SCRIPT)

    def is_allowed(self, key: str, tokens=1) -> bool:
        tokens_key = f"{self.key_prefix}:tokens:{key}"
        timestamp_key = f"{self.key_prefix}:time:{key}"
        now = time.time()
        
        result = self.script(
            keys=[tokens_key, timestamp_key],
            args=[self.rate, self.capacity, now, tokens]
        )
        return bool(result[0])

優(yōu)勢(shì)

  • 支持突發(fā)流量(burst)
  • 平滑限流,符合真實(shí)業(yè)務(wù)場(chǎng)景
  • Redis Lua 腳本保證高并發(fā)下的原子性

4.漏桶(Leaky Bucket)— 適合勻速處理

注:漏桶算法通常用于流量整形(如消息隊(duì)列),而非 Web API 限流,此處略。

三、生產(chǎn)級(jí)限流方案設(shè)計(jì)

多維度限流策略

維度示例工具
全局限流整個(gè)服務(wù) QPS ≤ 10,000Nginx + Lua
用戶級(jí)限流每個(gè)用戶 100 次/分鐘Redis + Token Bucket
IP 限流單 IP 50 次/秒FastAPI Middleware
接口級(jí)限流/pay 接口 10 次/秒裝飾器
業(yè)務(wù)級(jí)限流用戶 A 每天最多發(fā) 5 條短信數(shù)據(jù)庫(kù)計(jì)數(shù)

分布式限流架構(gòu)

graph LR
    A[Client] --> B[Nginx/LB]
    B --> C[Service Instance 1]
    B --> D[Service Instance 2]
    C & D --> E
    E --> F[Token Bucket State]

關(guān)鍵:所有實(shí)例共享 Redis 狀態(tài),實(shí)現(xiàn)集群級(jí)限流

四、FastAPI 集成示例(推薦)

創(chuàng)建限流中間件

# rate_limiter.py
from fastapi import Request, HTTPException, status
from starlette.middleware.base import BaseHTTPMiddleware
from .token_bucket import TokenBucketRateLimiter  # 上述實(shí)現(xiàn)

redis_client = redis.Redis(host="localhost", port=6379, decode_responses=True)
limiter = TokenBucketRateLimiter(
    redis_client, 
    key_prefix="api", 
    rate=10,      # 10 token/秒
    capacity=20   # 允許突發(fā) 20 次
)

class RateLimitMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # 獲取限流 key(可按 IP、用戶 ID、路徑組合)
        client_ip = request.client.host
        path = request.url.path
        
        # 例如:按 IP + 路徑限流
        rate_key = f"{client_ip}:{path}"
        
        if not limiter.is_allowed(rate_key):
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="Too Many Requests",
                headers={"Retry-After": "1"}  # 建議重試時(shí)間
            )
        
        response = await call_next(request)
        return response

在 FastAPI 應(yīng)用中啟用

# main.py
from fastapi import FastAPI
from rate_limiter import RateLimitMiddleware

app = FastAPI()
app.add_middleware(RateLimitMiddleware)

@app.get("/hello")
async def hello():
    return {"message": "Hello World"}

接口級(jí)精細(xì)限流(裝飾器)

from functools import wraps

def rate_limit(rate: float, capacity: int, key_func=None):
    def decorator(func):
        limiter = TokenBucketRateLimiter(
            redis_client, 
            key_prefix=f"func:{func.__name__}", 
            rate=rate, 
            capacity=capacity
        )
        
        @wraps(func)
        async def wrapper(*args, **kwargs):
            # 從請(qǐng)求中提取 key(需根據(jù)框架調(diào)整)
            request = kwargs.get("request") or args[0]
            key = key_func(request) if key_func else request.client.host
            
            if not limiter.is_allowed(key):
                raise HTTPException(429, "Rate limit exceeded")
            
            return await func(*args, **kwargs)
        return wrapper
    return decorator

# 使用
@app.post("/send-sms")
@rate_limit(rate=1/60, capacity=1)  # 每用戶每分鐘 1 次
async def send_sms(request: Request, phone: str):
    # 發(fā)送短信邏輯
    pass

五、高可用與監(jiān)控

降級(jí)策略

  • Redis 不可用時(shí):切換到本地內(nèi)存限流(如 cachetools.TTLCache
  • 配置動(dòng)態(tài)調(diào)整:通過(guò) Consul/Etcd 動(dòng)態(tài)修改限流參數(shù)
# 降級(jí)示例
try:
    allowed = redis_limiter.is_allowed(key)
except redis.ConnectionError:
    allowed = local_limiter.is_allowed(key)  # 本地限流

監(jiān)控指標(biāo)

  • 限流拒絕率rate_limit_rejected / total_requests
  • 桶填充率:監(jiān)控令牌消耗速度
  • 告警規(guī)則:拒絕率 > 5% 持續(xù) 5 分鐘
# Prometheus 指標(biāo)
from prometheus_client import Counter

RATE_LIMIT_REJECTED = Counter(
    "rate_limit_rejected_total", 
    "Total number of rate limited requests",
    ["endpoint", "client"]
)

# 在限流拒絕時(shí)增加計(jì)數(shù)
if not allowed:
    RATE_LIMIT_REJECTED.labels(endpoint=path, client=ip).inc()
    raise HTTPException(429, ...)

六、性能壓測(cè)對(duì)比(10,000 QPS)

方案CPU 使用率P99 延遲內(nèi)存占用準(zhǔn)確性
固定窗口35%8ms?(臨界問(wèn)題)
滑動(dòng)窗口65%25ms?
令牌桶(Lua)40%12ms?
無(wú)限流95%200ms+極高-

結(jié)論令牌桶 + Redis Lua 是生產(chǎn)環(huán)境最佳選擇

七、安全增強(qiáng)

防止限流繞過(guò)

  • Key 設(shè)計(jì):使用 user_id + ip + user_agent 組合,防代理池繞過(guò)
  • 黑名單機(jī)制:對(duì)惡意 IP 永久封禁

限流響應(yīng)規(guī)范

HTTP/1.1 429 Too Many Requests
Retry-After: 5
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708761600

八、總結(jié):限流決策樹(shù)

graph TD
    A[需要限流?] -->|是| B{數(shù)據(jù)一致性要求高?}
    B -->|是| C[用 Redis Token Bucket]
    B -->|否| D[用本地內(nèi)存限流]
    C --> E[寫(xiě) Lua 腳本保證原子性]
    E --> F[多維度 Key 設(shè)計(jì)]
    F --> G[集成監(jiān)控告警]
    G --> H[配置動(dòng)態(tài)調(diào)整]

終極建議

  • 新項(xiàng)目直接用 Token Bucket + Redis
  • 關(guān)鍵接口單獨(dú)配置限流策略
  • 永遠(yuǎn)返回 429 而非 500
  • 監(jiān)控比限流本身更重要

以上就是從原理到生產(chǎn)落地詳解Python高并發(fā)服務(wù)限流的終極方案的詳細(xì)內(nèi)容,更多關(guān)于Python高并發(fā)服務(wù)限流的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Python實(shí)現(xiàn)獲取網(wǎng)頁(yè)指定內(nèi)容

    使用Python實(shí)現(xiàn)獲取網(wǎng)頁(yè)指定內(nèi)容

    在當(dāng)今互聯(lián)網(wǎng)時(shí)代,網(wǎng)頁(yè)數(shù)據(jù)抓取是一項(xiàng)非常重要的技能,本文將帶你從零開(kāi)始學(xué)習(xí)如何使用Python獲取網(wǎng)頁(yè)中的指定內(nèi)容,希望對(duì)大家有所幫助
    2025-03-03
  • Python獲取昨天、今天、明天開(kāi)始、結(jié)束時(shí)間戳的方法

    Python獲取昨天、今天、明天開(kāi)始、結(jié)束時(shí)間戳的方法

    今天小編就為大家分享一篇Python獲取昨天、今天、明天開(kāi)始、結(jié)束時(shí)間戳的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • Python調(diào)用C語(yǔ)言的實(shí)現(xiàn)

    Python調(diào)用C語(yǔ)言的實(shí)現(xiàn)

    這篇文章主要介紹了Python調(diào)用C語(yǔ)言的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • python+ollama自己寫(xiě)代碼調(diào)用本地deepseek模型

    python+ollama自己寫(xiě)代碼調(diào)用本地deepseek模型

    本文主要介紹了python+ollama自己寫(xiě)代碼調(diào)用本地deepseek模型,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • Python3搭建http服務(wù)器的實(shí)現(xiàn)代碼

    Python3搭建http服務(wù)器的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Python3搭建http服務(wù)器的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • python 追蹤except信息方式

    python 追蹤except信息方式

    這篇文章主要介紹了python 追蹤except信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-04-04
  • Python實(shí)現(xiàn)多張圖片合成文字的效果

    Python實(shí)現(xiàn)多張圖片合成文字的效果

    前段時(shí)間看到有人問(wèn)如何使用Python實(shí)現(xiàn)多張圖片組成文字的效果?覺(jué)得還挺有意思,于是嘗試做了一下,剛好趕上端午節(jié),所以打算從網(wǎng)上下載1000張王心凌的照片,組成端午安康的字樣,感興趣的可以了解一下
    2022-06-06
  • 利用Python實(shí)現(xiàn)高效數(shù)據(jù)收集與挖掘的實(shí)戰(zhàn)指南

    利用Python實(shí)現(xiàn)高效數(shù)據(jù)收集與挖掘的實(shí)戰(zhàn)指南

    在當(dāng)今數(shù)據(jù)驅(qū)動(dòng)的時(shí)代,如何高效獲取互聯(lián)網(wǎng)上的海量數(shù)據(jù)成為許多企業(yè)和研究者的核心需求,Python憑借其豐富的爬蟲(chóng)庫(kù)和簡(jiǎn)潔的語(yǔ)法,成為了數(shù)據(jù)采集領(lǐng)域的首選工具,本文將帶你全面了解如何利用Python爬蟲(chóng)技術(shù)實(shí)現(xiàn)數(shù)據(jù)收集,需要的朋友可以參考下
    2025-07-07
  • Python selenium 加載并保存QQ群成員,去除其群主、管理員信息的示例代碼

    Python selenium 加載并保存QQ群成員,去除其群主、管理員信息的示例代碼

    這篇文章主要介紹了Python selenium 加載并保存QQ群成員 去除其群主、管理員信息的示例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-05-05
  • Python使用ConfigParser模塊操作配置文件的方法

    Python使用ConfigParser模塊操作配置文件的方法

    這篇文章主要介紹了Python使用ConfigParser模塊操作配置文件的方法,結(jié)合實(shí)例形式分析了Python基于ConfigParser模塊針對(duì)配置文件的創(chuàng)建、讀取、寫(xiě)入、判斷等相關(guān)操作技巧,需要的朋友可以參考下
    2018-06-06

最新評(píng)論

湖州市| 突泉县| 克山县| 海伦市| 长治市| 石阡县| 开原市| 开鲁县| 鄂托克前旗| 驻马店市| 肇源县| 长春市| 贡嘎县| 大名县| 云阳县| 黑山县| 和平区| 信阳市| 莱州市| 济阳县| 晋州市| 宁阳县| 昆明市| 莱芜市| 汕尾市| 安新县| 枣阳市| 沙坪坝区| 梨树县| 兴山县| 昌宁县| 泉州市| 钟山县| 桐梓县| 裕民县| 大埔区| 曲阜市| 塔河县| 老河口市| 新河县| 新建县|