Skip to content

第 14 章 · asyncio 支持

本章目标:了解 Locust 的 gevent 并发模型与 asyncio 生态的关系,掌握 aiolocust 的用法,并能根据场景选择合适方案。

14.1 Locust 的并发模型:gevent

Locust 的传统并发基于 gevent——一个 Python 协作式并发的库,通过 monkey-patching 将标准库 I/O 操作替换为非阻塞版本。每个虚拟 User 运行在一个 greenlet(轻量级协程)中:

python
# locust 启动时自动执行 monkey-patch
from gevent import monkey
monkey.patch_all()  # 替换 socket、time、threading 等模块的阻塞调用

# 此后所有 User 实例运行在 greenlet 中,I/O 等待时自动让出 CPU

gevent 方案的优势:对用户代码透明——你写同步代码,Locust 自动实现并发。劣势是 monkey-patching 对 C 扩展库无效(见第 13 章)。

14.2 aiolocust:asyncio 重新设计

aiolocust 是 2026 年发布的 Locust 生态新项目,用 asyncio/aiohttp 全面替代 gevent/requests 技术栈,拥抱现代 Python 异步标准。

bash
pip install aiolocust

基本用法:

python
import asyncio
from aiolocust import HttpUser

async def run(user: HttpUser):
    async with user.client.get("http://example.com/") as resp:
        pass  # 处理响应

    async with user.client.get("http://example.com/") as resp:
        # 额外校验,不只是 HTTP 状态码:
        assert "expected text" in await resp.text()

    await asyncio.sleep(0.1)

项目状态

aiolocust 目前作为独立项目维护,功能比 Locust 主线少(更极简),依赖 OTEL 做可视化分析。未来可能合并进 Locust 或随主版本发布。

14.3 gevent vs asyncio 对比

维度gevent (Locust)asyncio (aiolocust)
并发模型greenlet + monkey-patch原生 async/await
学习成本低(写同步代码)中(需理解异步编程)
C 扩展兼容❌ 无法 patch✅ 原生支持
HTTP 客户端requestsaiohttp
WebSocket需额外库aiohttp 内置
生态系统成熟稳定快速发展
适用场景传统 HTTP API 压测高并发、C 库依赖、WebSocket

选型决策树

text
你的压测需要什么?
├── 只测 HTTP/HTTPS API → 用 Locust(gevent),生态最成熟
├── 需要 C 扩展库(psycopg2 等) → 考虑 aiolocust
├── 大量 WebSocket 长连接 → aiolocust 或自定义扩展
├── 团队熟悉 asyncio → aiolocust 更自然
└── 需要完整 Web UI 和分布式 → Locust(功能更全)

14.4 在 Locust 中混合使用异步代码

即使不用 aiolocust,也可以在 Locust 任务中调用异步函数(如 asyncpg 数据库查询):

python
import asyncio
from locust import User, task, between

class AsyncDbUser(User):
    wait_time = between(0.5, 2)

    def on_start(self):
        """在事件循环中创建异步任务"""
        self.loop = asyncio.new_event_loop()
        self.task_thread = threading.Thread(
            target=self.loop.run_forever, daemon=True
        )
        self.task_thread.start()

    def _run_async(self, coro):
        """在线程的事件循环中执行协程"""
        return asyncio.run_coroutine_threadsafe(coro, self.loop).result()

    @task
    def query_database(self):
        start = time.perf_counter()
        result = self._run_async(self._async_query())
        elapsed = (time.perf_counter() - start) * 1000
        environment.events.request.fire(
            request_type="ASYNC-DB", name="query",
            response_time=elapsed, response_length=len(str(result)),
            exception=None, context={},
        )

    async def _async_query(self):
        # 这里可以用 asyncpg / aiohttp 等 asyncio 库
        conn = await asyncpg.connect(dsn="postgresql://localhost/test")
        rows = await conn.fetch("SELECT * FROM users LIMIT 10")
        await conn.close()
        return rows

性能注意

run_coroutine_threadsafe 有线程切换开销。如果大量使用此模式,建议直接评估 aiolocust。

本章小结

  • Locust 主线基于 gevent,monkey-patching 让同步代码自动获得并发能力;
  • aiolocust 用 asyncio/aiohttp 重新实现了 Locust 核心概念,支持 C 扩展库;
  • 两者各有优势:gevent 生态成熟、asyncio 更现代化;
  • 可通过 run_coroutine_threadsafe 在 Locust 中桥接异步库。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. Locust 传统并发模型基于哪个库?

2. aiolocust 相比传统 Locust 的核心变化是什么?

3. 以下哪种场景最适合选择 aiolocust?

4. 在 Locust 中调用异步函数的正确方式是?

🛠️ 动手实践

  1. 安装 aiolocust 并编写一个简单的压测脚本,与同场景下的 Locust 脚本对比 RPS 表现。
  2. 在 Locust 中通过 run_coroutine_threadsafe 桥接 aiohttp 发起请求。
  3. 写一份选型备忘录:对比 gevent 与 asyncio 在你们团队技术栈中的优劣。

下一章学习将 Locust 作为库嵌入 你自己的 Python 项目和 CI 流水线。