测试标题001

0 阅读2分钟

测试标题001:Python 异步任务队列的工程化实践

一、背景与痛点

在现代后端服务中,耗时操作(邮件发送、图片处理、第三方回调)如果直接阻塞在请求链路里,会拖垮接口时延。以 Python 为例,requests 同步调用在 QPS 较高时容易造成线程堆积。本文用一个轻量方案演示:基于 asyncio + 内存队列实现任务异步化,并给出工程化的降级与监控思路。

二、核心设计

整体分为三部分:

  1. 任务模型:定义统一的数据结构,携带任务类型、参数、重试次数。
  2. 异步队列:生产者投递任务,消费者循环拉取执行。
  3. 降级策略:队列满时走同步兜底,避免丢任务。
from dataclasses import dataclass, field
from typing import Any, Callable
import asyncio
import logging

logger = logging.getLogger("task_queue")

@dataclass
class Task:
    name: str
    payload: dict = field(default_factory=dict)
    retry: int = 3

三、生产者与消费者

class AsyncTaskQueue:
    def __init__(self, maxsize: int = 1000):
        self.queue: asyncio.Queue = asyncio.Queue(maxsize=maxsize)
        self.handlers: dict[str, Callable] = {}

    def register(self, name: str, handler: Callable):
        self.handlers[name] = handler

    async def push(self, task: Task) -> bool:
        try:
            self.queue.put_nowait(task)
            return True
        except asyncio.QueueFull:
            logger.warning("队列已满,任务 %s 走同步兜底", task.name)
            return False

    async def worker(self):
        while True:
            task: Task = await self.queue.get()
            try:
                await self._run_with_retry(task)
            finally:
                self.queue.task_done()

    async def _run_with_retry(self, task: Task):
        handler = self.handlers.get(task.name)
        if handler is None:
            logger.error("未注册处理器: %s", task.name)
            return
        for attempt in range(task.retry):
            try:
                await handler(task.payload)
                return
            except Exception as exc:
                logger.exception("任务 %s 第 %d 次失败: %s", task.name, attempt + 1, exc)
                await asyncio.sleep(2 ** attempt)

四、业务接入示例

async def send_email(payload: dict):
    # 模拟异步邮件发送
    await asyncio.sleep(0.1)
    print(f"发送邮件给 {payload['to']}")

async def main():
    queue = AsyncTaskQueue(maxsize=100)
    queue.register("send_email", send_email)

    consumer = asyncio.create_task(queue.worker())

    ok = await queue.push(Task("send_email", {"to": "user@example.com"}))
    if not ok:
        # 降级:直接同步执行
        await send_email({"to": "user@example.com"})

    await queue.queue.join()
    consumer.cancel()

asyncio.run(main())

五、测试与验证

import asyncio
import pytest

@pytest.mark.asyncio
async def test_push_and_execute():
    queue = AsyncTaskQueue()
    executed = []
    queue.register("echo", lambda p: executed.append(p))
    await queue.push(Task("echo", {"v": 1}))
    await queue.queue.join()   # 等待消费完成
    assert executed == [{"v": 1}]

运行测试:pytest -q,结果应显示 1 passed。

六、工程化注意点

  • 并发控制:生产环境应限制 worker 数量,避免无界占用协程与连接池。
  • 持久化:内存队列重启即丢,重要任务建议写入 Redis Stream 或消息中间件。
  • 监控指标:记录队列深度、消费耗时、失败率,接入 Prometheus。
  • 超时保护:单任务应设置超时,防止挂起拖垮消费者。

以上方案在小流量场景下可快速落地,兼顾简洁性与可扩展性。

海量精选技术文档和实战案例持续更新,敬请关注【风骏时光少年】