客户端发送http请求进行流量控制

客户端发送http请求进行流量控制

实现方式 1:使用 Semaphore (信号量) 控制流量

asyncio.Semaphore 是一种简单的流控方法,可以用来限制并发请求数量。

python 复制代码
import asyncio
import aiohttp
import time

class HttpClientWithSemaphore:
    def __init__(self, max_concurrent_requests=5, request_period=10):
        self.max_concurrent_requests = max_concurrent_requests
        self.request_period = request_period
        self.semaphore = asyncio.Semaphore(max_concurrent_requests)
        self.session = aiohttp.ClientSession()

    async def fetch(self, url):
        async with self.semaphore:
            try:
                async with self.session.get(url) as response:
                    return await response.text()
            except Exception as e:
                print(f"Request failed: {e}")
                return None

    async def close(self):
        await self.session.close()

async def main_with_semaphore():
    client = HttpClientWithSemaphore(max_concurrent_requests=5)
    urls = [
        "http://example.com/api/1",
        "http://example.com/api/2",
        "http://example.com/api/3",
        "http://example.com/api/4",
        "http://example.com/api/5",
        "http://example.com/api/6",
    ]

    tasks = [client.fetch(url) for url in urls]
    responses = await asyncio.gather(*tasks)

    for response in responses:
        if response:
            print(response)

    await client.close()

if __name__ == "__main__":
    asyncio.run(main_with_semaphore())

优点

  • 简单易实现,使用内置的 asyncio.Semaphore 就能限制并发请求数量。
  • 易于维护,代码简单清晰。

缺点

  • 缺少精细的流控机制,例如每 10 秒内限制请求数量(只能控制总并发数量)。
  • 难以适应更加复杂的流控需求。

实现方式 2:使用滑动窗口 (Sliding Window) 算法

滑动窗口算法是一种可以精确控制在一定时间内的请求数量的机制。它能平滑地调整速率。

python 复制代码
import asyncio
import aiohttp
from collections import deque
import time

class SlidingWindowRateLimiter:
    def __init__(self, max_requests, window_seconds):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.timestamps = deque()

    async def acquire(self):
        current_time = time.monotonic()
        # 清理超出窗口时间的旧请求
        while self.timestamps and current_time - self.timestamps[0] > self.window_seconds:
            self.timestamps.popleft()

        if len(self.timestamps) < self.max_requests:
            self.timestamps.append(current_time)
            return True
        else:
            # 计算需要等待的时间
            sleep_time = self.window_seconds - (current_time - self.timestamps[0])
            await asyncio.sleep(sleep_time)
            return await self.acquire()

class HttpClientWithSlidingWindow:
    def __init__(self, max_requests_per_period=5, period=10):
        self.rate_limiter = SlidingWindowRateLimiter(max_requests_per_period, period)
        self.session = aiohttp.ClientSession()

    async def fetch(self, url):
        await self.rate_limiter.acquire()
        try:
            async with self.session.get(url) as response:
                return await response.text()
        except Exception as e:
            print(f"Request failed: {e}")
            return None

    async def close(self):
        await self.session.close()

async def main_with_sliding_window():
    client = HttpClientWithSlidingWindow(max_requests_per_period=5, period=10)
    urls = [
        "http://example.com/api/1",
        "http://example.com/api/2",
        "http://example.com/api/3",
        "http://example.com/api/4",
        "http://example.com/api/5",
        "http://example.com/api/6",
    ]

    tasks = [client.fetch(url) for url in urls]
    responses = await asyncio.gather(*tasks)

    for response in responses:
        if response:
            print(response)

    await client.close()

if __name__ == "__main__":
    asyncio.run(main_with_sliding_window())

优点

  • 更加精确地控制时间窗口内的请求数量。
  • 平滑控制请求速率,适用于需要稳定流量的情况。

缺点

  • 实现稍复杂,需要维护一个请求时间戳队列。
  • 在极端条件下,如果有大量请求积压,可能会造成延迟波动。

实现方式 3:使用 aiolimiter 第三方库

aiolimiter 是一个专门用于异步流控的 Python 库,支持令牌桶和滑动窗口算法。

安装 aiolimiter

bash 复制代码
pip install aiolimiter

代码示例

python 复制代码
import asyncio
import aiohttp
from aiolimiter import AsyncLimiter

class HttpClientWithAiolimiter:
    def __init__(self, max_requests_per_period=5, period=10):
        # 初始化流控器,每10秒允许5个请求
        self.limiter = AsyncLimiter(max_requests_per_period, period)
        self.session = aiohttp.ClientSession()

    async def fetch(self, url):
        async with self.limiter:
            try:
                async with self.session.get(url) as response:
                    return await response.text()
            except Exception as e:
                print(f"Request failed: {e}")
                return None

    async def close(self):
        await self.session.close()

async def main_with_aiolimiter():
    client = HttpClientWithAiolimiter(max_requests_per_period=5, period=10)
    urls = [
        "http://example.com/api/1",
        "http://example.com/api/2",
        "http://example.com/api/3",
        "http://example.com/api/4",
        "http://example.com/api/5",
        "http://example.com/api/6",
    ]

    tasks = [client.fetch(url) for url in urls]
    responses = await asyncio.gather(*tasks)

    for response in responses:
        if response:
            print(response)

    await client.close()

if __name__ == "__main__":
    asyncio.run(main_with_aiolimiter())

优点

  • 使用方便,aiolimiter 直接支持流控机制。
  • 代码简洁且配置灵活,可直接设置流控参数。
  • 第三方库已经过优化,适合快速开发。

缺点

  • 依赖于外部库,需要额外安装。
  • 灵活性相对有限,无法完全控制算法的细节。

比较总结

实现方式 优点 缺点 适用场景
信号量控制 (Semaphore) 简单易实现,易于维护 控制粒度较粗,不适合复杂流控 适合简单并发控制场景
滑动窗口 (Sliding Window) 精确控制时间窗口内的请求数量,平滑控制请求速率 实现稍复杂,可能出现延迟波动 适合需要精确流控的场景
aiolimiter 第三方库 使用方便,代码简洁,库优化良好 依赖外部库,灵活性相对有限 适合快速实现流控的项目

希望这些不同的实现方式和比较能够帮助你选择适合的 HTTP 客户端实现方案。如果你对某种实现方式有特别的需求或疑问,请随时告知!

相关推荐
熬夜敲代码的小N8 分钟前
仓颉ArrayList动态数组源码分析:从底层实现到性能优化
数据结构·python·算法·ai·性能优化
yumgpkpm9 分钟前
Hadoop大数据平台在中国AI时代的后续发展趋势研究CMP(类Cloudera CDP 7.3 404版华为鲲鹏Kunpeng)
大数据·hive·hadoop·python·zookeeper·oracle·cloudera
沉默媛35 分钟前
如何下载安装以及使用labelme,一个可以打标签的工具,实现数据集处理,详细教程
图像处理·人工智能·python·yolo·计算机视觉
HMS Core44 分钟前
【FAQ】HarmonyOS SDK 闭源开放能力 — Push Kit
linux·python·华为·harmonyos
CODE_RabbitV1 小时前
【1min 速通 -- PyTorch 张量数据类型】张量类型的获取、转化与判别
人工智能·pytorch·python
程序猿20232 小时前
Python每日一练---第九天:H指数
开发语言·python
武陵悭臾2 小时前
Python应用开发学习:Pygame中实现切换开关及鼠标拖动连续填充功能
python·学习·程序人生·个人开发·pygame
JELEE.2 小时前
Django中的clean()方法和full_clean()方法
后端·python·django
2401_841495642 小时前
【LeetCode刷题】移动零
数据结构·python·算法·leetcode·数组·双指针法·移动零
quant_19862 小时前
全面解析美股行情API
经验分享·后端·python·websocket·程序人生·区块链