讲师中心 微信公众号
AI工具推荐 视频效率加速

使用 asyncio 实现无固定任务列表的持续异步请求调度

胖磊君_9359

胖磊君_9359

发布时间:2026-05-20 11:20:11

|

921人浏览过

|

来源于php中文网

原创

本文介绍如何摆脱 asyncio.gather() 对预定义任务列表的依赖,构建一个可长期运行、低延迟、无缓存、支持动态请求生成的异步 HTTP 请求调度器,兼顾服务器负载控制与会话隔离。

本文介绍如何摆脱 `asyncio.gather()` 对预定义任务列表的依赖,构建一个可长期运行、低延迟、无缓存、支持动态请求生成的异步 http 请求调度器,兼顾服务器负载控制与会话隔离。

在实际爬虫或服务轮询场景中,常见需求是“持续按固定间隔发起请求”,但传统 asyncio.gather(tasks) 要求一次性构造全部协程对象——这不仅违背流式调度逻辑,还会造成内存累积、响应延迟(如每轮 gather 执行完才启动下一轮),且难以实现请求节流、会话轮换与缓存规避。

正确的解法是:用 asyncio.create_task() 动态创建并管理任务,配合 asyncio.sleep() 精确控制间隔,结合 aiohttp 实现真正非阻塞、会话隔离、无共享缓存的异步请求流。

以下是一个生产就绪的示例架构:

import asyncio
import aiohttp
import time
from loguru import logger
from typing import AsyncIterator, Dict, Any

# 全局配置(避免重复加载)
URL = "https://api.example.com/endpoint"
REQUEST_INTERVAL = 0.04  # 秒级间隔,可动态调整
MAX_CONCURRENT = 5         # 并发上限,防压垮服务

class AuthSession:
    """轻量会话封装,确保每次请求使用独立 headers/cookies"""
    def __init__(self, auth_path: str):
        self.auth_path = auth_path
        # 实际中从文件/DB加载认证凭据,不复用 session 对象

    def get_headers(self) -> Dict[str, str]:
        return {"User-Agent": "AsyncBot/1.0", "X-Request-ID": f"{int(time.time() * 1000)}"}

    def get_cookies(self) -> Dict[str, str]:
        return {"session_id": f"sess_{hash(self.auth_path) % 1000000}"}

async def fetch_with_retry(
    session: aiohttp.ClientSession,
    url: str,
    method: str = "GET",
    headers: Dict[str, str] = None,
    cookies: Dict[str, str] = None,
    json: Dict[str, Any] = None,
    max_retries: int = 3
) -> aiohttp.ClientResponse:
    for attempt in range(max_retries):
        try:
            async with session.request(
                method=method,
                url=url,
                headers=headers,
                cookies=cookies,
                json=json,
                # 关键:禁用客户端缓存
                params={"t": str(int(time.time() * 1000))},  # 时间戳参数强制刷新
                timeout=aiohttp.ClientTimeout(total=15)
            ) as resp:
                resp.raise_for_status()
                return resp
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            if attempt == max_retries - 1:
                logger.error("Request failed after {n} retries: {url}", n=max_retries, url=url)
                raise e
            await asyncio.sleep(1 * (2 ** attempt))  # 指数退避
    raise RuntimeError("Unreachable")

async def request_worker(
    worker_id: int,
    auth_gen: AsyncIterator[AuthSession],
    proxy: Dict[str, str] = None
):
    """单个持续工作的协程:无限循环 + 动态会话 + 精确间隔"""
    connector = aiohttp.TCPConnector(
        limit_per_host=MAX_CONCURRENT,
        ttl_dns_cache=300,
        keepalive_timeout=15
    )

    async with aiohttp.ClientSession(connector=connector) as session:
        while True:
            try:
                # 动态获取会话(模拟轮换账号)
                acc = await anext(auth_gen)
                headers = acc.get_headers()
                cookies = acc.get_cookies()

                # 第一次请求:获取基础数据
                resp1 = await fetch_with_retry(
                    session, URL + "/list", "GET", headers, cookies
                )
                data = await resp1.json()

                # 解析并触发后续请求(如需)
                for item in data.get("items", [])[:3]:  # 限制每轮请求数
                    info1 = item.get("id")
                    info2 = item.get("type")

                    # 避免重复处理(可用 Redis 或内存 set 去重)
                    if not hasattr(request_worker, "_seen"):
                        request_worker._seen = set()
                    if info1 in request_worker._seen:
                        continue
                    request_worker._seen.add(info1)

                    # 异步触发二级请求(不等待,fire-and-forget)
                    asyncio.create_task(
                        secondary_request(session, acc, info1, info2)
                    )

                logger.debug("Worker {id} completed cycle", id=worker_id)

            except Exception as e:
                logger.exception("Worker {id} error: {e}", id=worker_id, e=e)

            # 严格间隔:sleep 到下一个调度点(补偿执行耗时)
            await asyncio.sleep(REQUEST_INTERVAL)

async def secondary_request(
    session: aiohttp.ClientSession,
    acc: AuthSession,
    info1: str,
    info2: str
):
    """辅助请求,完全异步执行,不影响主循环节奏"""
    try:
        payload = {"ref_id": info1, "category": info2}
        resp = await fetch_with_retry(
            session, URL + "/detail", "POST", acc.get_headers(), acc.get_cookies(), json=payload
        )
        result = await resp.json()
        logger.info("Detail fetched for {id}: {code}", id=info1, code=result.get("code"))
    except Exception as e:
        logger.warning("Secondary request failed for {id}: {e}", id=info1, e=e)

# 会话生成器(支持无限、轮换、懒加载)
async def auth_session_generator() -> AsyncIterator[AuthSession]:
    auth_paths = ["accounts/acc1.json", "accounts/acc2.json"]
    while True:
        for path in auth_paths:
            yield AuthSession(path)
            await asyncio.sleep(0)  # 让出控制权,避免饥饿

# 主调度入口
async def main():
    logger.info("Starting async request scheduler...")

    # 启动多个独立 worker(模拟多代理/多账号并发)
    proxy_list = [None]  # 可扩展为真实代理池
    workers = []

    for i, proxy in enumerate(proxy_list):
        auth_gen = auth_session_generator()
        task = asyncio.create_task(
            request_worker(worker_id=i, auth_gen=auth_gen, proxy=proxy)
        )
        workers.append(task)

    # 保持主协程活跃(可加入信号监听、热重载等)
    try:
        await asyncio.gather(*workers)
    except KeyboardInterrupt:
        logger.info("Shutting down gracefully...")
        for task in workers:
            task.cancel()
        await asyncio.gather(*workers, return_exceptions=True)

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

✅ 关键优势说明:

  • 无预定义任务:create_task() 动态创建,while True 实现无限流式调度;
  • 低延迟响应:每轮仅 sleep(0.04),不等待整批完成,新请求随时插入;
  • 零缓存干扰:通过时间戳参数 ?t=... + aiohttp 默认禁用缓存 + 独立 ClientSession;
  • 负载可控:TCPConnector.limit_per_host 限流 + REQUEST_INTERVAL 全局节制 + 指数退避容错;
  • 会话隔离:每个请求使用全新 headers/cookies,无跨请求状态污染;
  • 可观测性强:结构化日志、错误隔离、worker 级别监控。

⚠️ 注意事项:

  • 避免在 request_worker 中使用 time.sleep()(会阻塞整个事件循环),务必用 await asyncio.sleep();
  • 内存去重(_seen)仅适用于短期轻量场景;高吞吐建议接入 Redis 的 SETNX 或布隆过滤器;
  • 若目标接口有反爬,请将 User-Agent、Referer、请求头指纹等纳入 AuthSession 动态生成;
  • 生产环境应添加 asyncio.CancelledError 处理,并在退出前关闭所有连接器。

该模式已广泛用于实时行情拉取、分布式任务分发、API 健康巡检等场景,兼具弹性、可观测性与工程健壮性。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

1731

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

4404

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

1709

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

25757

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

3127

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

3147

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

1183

2023.08.11

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

596

2023.08.10

VibeKnow知识视频制作与导出详解
VibeKnow知识视频制作与导出详解

本专题整理VibeKnow从注册、素材上传、AI生成到预览导出的完整操作,帮助新手快速制作第一条知识讲解视频。

0

2026.10.14

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn