客户端发送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 客户端实现方案。如果你对某种实现方式有特别的需求或疑问,请随时告知!

相关推荐
老师好,我是刘同学21 分钟前
Python执行命令并保存输出到文件
python
啵啵鱼爱吃小猫咪2 小时前
机械臂阻抗控制github项目-mujoco仿真
开发语言·人工智能·python·机器人
MaximusCoder2 小时前
等保测评命令——Centos Linux
linux·运维·经验分享·python·安全·centos
yunyun321232 小时前
用Python生成艺术:分形与算法绘图
jvm·数据库·python
万象.2 小时前
Linux数据链路层通信原理及报文格式
linux·网络·网络协议
m0_662577972 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
songyuc3 小时前
【PyTorch】感觉`CrossEntropyLoss`和`BCELoss`很类似,为什么它们接收labels的shape常常不一样呢?
人工智能·pytorch·python
ℳ๓₯㎕.空城旧梦3 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
浩子智控4 小时前
python程序打包的文件地址处理
开发语言·python·pyqt
Jackey_Song_Odd4 小时前
Part 1:Python语言核心 - 序列与容器
开发语言·windows·python