使用aiohttp

构建异步Web服务入门

为什么选择它?

在 Python 的异步编程世界里,asyncio 是标准库中单线程并发 I/O 的核心框架。而 aiohttp 则是这个生态下最成熟的 异步 HTTP 客户端 + 服务器双端库。如果你写惯了 Flask、django 这类同步框架,可能会觉得高并发只能靠多线程/多进程硬扛;但 aiohttp 不需要这些额外开销,就能用单线程轻松 hold 住上千个并发连接。

它的主要优势:

  • 轻量高并发:完全异步非阻塞,内存占用远低于同步多线程方案。
  • 原生长连接支持:对 WebSocket、SSE 等实时通信场景支持良好。
  • 全双工 HTTP:既能写服务器,也能当 HTTP 客户端,一套依赖解决前后端通信。
  • 生态友好:与 asyncio 深度集成,可搭配 uvloopaiohttp-sessionaiohttp-jinja2 等扩展。

非常适合用来构建 API 网关、实时消息推送、异步爬虫服务、BFF 层等 I/O 密集型应用。


环境准备

  1. 基础要求
    Python 3.7 及以上版本(asyncio 在 3.7 后引入了更简洁的 async/await 语法增强和更稳定的事件循环实现,推荐直接上 3.10+)。

  2. 安装 aiohttp

    pip install aiohttp

    如果后续需要性能调优,可以一并安装 uvloopgunicorn,但入门阶段只需 aiohttp


第一个异步Web服务器

让我们从一个最精简但功能完整的双路由服务器开始:

  • / → 返回一个带标题的 HTML 首页
  • /{nickname} → 根据 URL 中的昵称返回个性化问候
# app.py
from aiohttp import web

async def handle_index(request):
    """根路由异步处理函数:返回静态首页"""
    html_content = "<h1>✨ aiohttp 异步首页 ✨</h1><p>访问 /你的昵称 试试吧!</p>"
    # 指定 text/html 类型,避免浏览器直接展示纯文本
    return web.Response(text=html_content, content_type="text/html")

async def handle_hello(request):
    """动态昵称路由处理函数:获取 URL 参数"""
    # match_info 从 URL 占位符中提取值,未匹配到则默认为 '陌生访客'
    nickname = request.match_info.get("nickname", "陌生访客")
    html_content = f"<h1>👋 Hello, {nickname}!</h1>"
    return web.Response(text=html_content, content_type="text/html")

def make_app():
    """应用工厂函数:创建实例并绑定路由"""
    app = web.Application()
    # 批量添加 GET 请求的路由
    app.add_routes([
        web.get("/", handle_index),
        web.get("/{nickname}", handle_hello)
    ])
    return app

if __name__ == "__main__":
    # 启动本地开发服务器,监听 0.0.0.0:8080(局域网也能访问)
    app = make_app()
    web.run_app(app, host="0.0.0.0")

运行后,终端会输出类似:

======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)

浏览器访问 http://localhost:8080/http://localhost:8080/Alice,就能看到对应的问候页面了。


代码细节拆解

1. 异步处理函数

  • 必须是 async def:这样事件循环才能自由调度它,不会阻塞主线程。
  • 强制接收 web.Request 对象:该对象包含了所有请求信息——URL、请求头、Body、Cookie 等,处理逻辑都从这里开始。
  • 必须返回 web.Response 或其子类:不能直接返回字符串或字典,必须显式构造响应对象。常用的还有 web.json_response()web.FileResponse() 等。

2. 路由配置

  • 创建应用核心web.Application() 是整个服务的“大脑”。
  • 添加路由:支持 web.get()web.post()web.put()web.delete()web.patch() 等方法,也可以使用 app.router.add_route() 统一注册。
  • 批量 vs 单条:示例中用了 add_routes() 批量添加,适合路由较多时;少量路由也可以用 app.router.add_get() 等单独添加。
  • URL 占位符{nickname} 是动态部分,会自动注入到 request.match_info 中,还可以加正则约束,例如 {nickname:\w+} 只匹配字母数字下划线。

3. 应用工厂函数

示例中特意用 make_app() 而不是直接全局创建 app,这是生产环境的常见实践:

  • 隔离环境:可以为测试/开发/生产分别创建不同配置的实例。
  • 避免全局状态:避免模块级变量带来的隐式依赖。
  • 易于部署集成:与 Gunicorn、uWSGI 等服务器配合时,它们会调用这个工厂函数来获取应用实例。

运行与快速测试

1. 本地启动

python app.py

控制台会打印监听地址,按 Ctrl+C 即可停止服务。

2. 浏览器 / 命令行访问

  • 浏览器
    首页:http://localhost:8080/
    动态页:http://localhost:8080/Alice
  • 命令行 curl 测试
    # 首页
    curl http://localhost:8080/
    # 动态页
    curl http://localhost:8080/Bob
    你会看到返回的 HTML 内容,说明服务已经正常工作了。

几个常用的高级配置

自定义监听端口和地址

开发时可能只想本机访问,或者避开端口冲突:

if __name__ == "__main__":
    web.run_app(app, host="127.0.0.1", port=9000)

快速返回 JSON 响应

aiohttp 内置了 json_response,无需手动设置 Content-Type 和序列化:

from aiohttp import web

async def handle_api(request):
    data = {"code": 200, "msg": "请求成功", "data": {"nickname": "Charlie"}}
    return web.json_response(data)

路由中加上 web.get("/api", handle_api) 即可,客户端会收到标准的 JSON 字符串。

添加中间件

中间件可以对所有请求执行前置/后置统一逻辑,比如日志记录、权限校验、跨域设置等:

from aiohttp import web

async def log_middleware(request, handler):
    # 前置:记录请求方法和路径
    print(f"[REQUEST] {request.method} {request.path}")
    # 调用后续处理(下一个中间件或最终路由处理函数)
    response = await handler(request)
    # 后置:记录响应状态码
    print(f"[RESPONSE] {request.method} {request.path}{response.status}")
    return response

def make_app():
    # 初始化时传入中间件列表
    app = web.Application(middlewares=[log_middleware])
    app.add_routes([
        web.get("/", handle_index),
        web.get("/{nickname}", handle_hello),
        web.get("/api", handle_api)
    ])
    return app

现在每个请求都会在终端输出请求路径和最终响应码,非常便于调试。


生产环境部署建议

  1. 不要直接用 web.run_app() 上线
    它自带的服务器仅适合开发调试,性能和稳定性不足以应对线上流量。

  2. 推荐组合:Gunicorn + uvloop

    # 安装加速工具
    pip install uvloop gunicorn
    # 启动 4 个 worker 进程,每个 worker 使用 uvloop 事件循环
    gunicorn app:make_app \
        --bind 0.0.0.0:9000 \
        --worker-class aiohttp.GunicornUVLoopWebWorker \
        --workers 4
    • app:make_app 表示从 app 模块导入 make_app 工厂函数。
    • GunicornUVLoopWebWorker 会自动用 uvloop 替换默认事件循环,性能可提升 2~4 倍。
    • worker 数量一般设置为 CPU 核心数的 2 倍(视业务 I/O 密集程度调整)。
  3. 反向代理
    在前面再挂一层 Nginx,负责静态文件处理、SSL 终结、负载均衡和请求缓冲,让 Python 进程专注于动态逻辑。


总结

通过这篇教程,你掌握了 aiohttp 服务器端的核心用法:

  • 如何定义异步路由并返回 HTML / JSON 响应。
  • 如何利用中间件处理通用逻辑。
  • 如何为生产环境准备部署方案。

aiohttp 的能力远不止于此,它还内置了客户端(可用于异步爬虫)、WebSocket 支持,以及丰富的第三方扩展(如模板引擎、Session 管理)。如果你的项目需要轻量、高效、异步优先的 Web 服务,aiohttp 会是一个极佳的选择。