Use aiohttp

Getting started with building asynchronous web services

Why choose it?

In the world of asynchronous programming in Python,asyncioIt is the core framework for single-threaded concurrent I/O in the standard library. andaiohttpIt is the most mature asynchronous HTTP client + server double-ended library in this ecosystem. If you are used to writing synchronization frameworks such as Flask and Django, you may feel that high concurrency can only be achieved through multi-threading/multi-process; butaiohttpWithout these additional overheads, you can easily hold thousands of concurrent connections with a single thread.

Its main advantages:

  • Lightweight and High Concurrency: Completely asynchronous and non-blocking, the memory usage is much lower than that of synchronous multi-threading solutions.
  • Native growing connection support: Good support for real-time communication scenarios such as WebSocket and SSE.
  • Full-duplex HTTP: It can be used as both a server and an HTTP client. A set of dependencies solves the front-end and back-end communication.
  • ECO-FRIENDLY: withasyncioDeeply integrated, compatible withuvloopaiohttp-sessionaiohttp-jinja2Wait for expansion.

It is very suitable for building I/O-intensive applications such as API gateways, real-time message push, asynchronous crawler services, and BFF layers.


Environment preparation

  1. Basic Requirements Python 3.7 and above (asyncioIntroduced after 3.7, the more conciseasync/awaitSyntax enhancement and more stable event loop implementation, it is recommended to go directly to 3.10+).

  2. Install aiohttp

    pip install aiohttp

If you need performance tuning later, you can install it together.uvloopandgunicorn, but the entry stage only requiresaiohttp


The first asynchronous web server

Let's start with a minimal yet fully functional dual routing server:

  • /→ Returns an HTML homepage with title
  • /{nickname}→ Return a personalized greeting based on the nickname in the 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")

After running, the terminal will output something like:

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

Browser accesshttp://localhost:8080/andhttp://localhost:8080/Alice, you can see the corresponding greeting page.


Code details disassembly

1. Asynchronous processing function

  • Must beasync def: This way the event loop can schedule it freely without blocking the main thread.
  • Forced receptionweb.RequestObject: This object contains all request information - URL, request headers, Body, Cookie, etc. The processing logic starts here.
  • must be returnedweb.Responseor its subclasses: A string or dictionary cannot be returned directly, the response object must be explicitly constructed. Also commonly used areweb.json_response()web.FileResponse()wait.

2. Routing configuration

  • Create application core:web.Application()It is the "brain" of the entire service.
  • Add Route: Supportedweb.get()web.post()web.put()web.delete()web.patch()and other methods, you can also useapp.router.add_route()Unified registration.
  • Batch vs Single: used in the exampleadd_routes()Batch addition is suitable when there are many routes; it can also be used for a small number of routesapp.router.add_get()Wait to be added separately.
  • URL placeholder:{nickname}It is a dynamic part and will be automatically injected intorequest.match_info, you can also add regular constraints, such as{nickname:\w+}Matches only alphanumeric underscores.

3. Apply factory function

Specifically used in the examplemake_app()Instead of directly creating it globallyapp, which is a common practice in production environments:

  • Isolated environment: You can create instances with different configurations for testing/development/production.
  • Avoid global state: Avoid implicit dependencies caused by module-level variables.
  • Easy to deploy and integrate: When working with servers such as Gunicorn and uWSGI, they will call this factory function to obtain application instances.

Run and quick test

1. Local startup

python app.py

The console will print the listening address, pressCtrl+Cto stop the service.

2. Browser/command line access

  • Browser front page:http://localhost:8080/
    Dynamic page:http://localhost:8080/Alice
  • Command linecurltest
    # 首页
    curl http://localhost:8080/
    # 动态页
    curl http://localhost:8080/Bob

You will see the HTML content returned, indicating that the service is working properly.


Several commonly used advanced configurations

Customize the listening port and address

When developing, you may only want to access it locally, or avoid port conflicts:

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

Return JSON response quickly

aiohttpBuilt-injson_response, no need to manually setContent-Typeand serialization:

from aiohttp import web

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

Add to routeweb.get("/api", handle_api)That's it, the client will receive a standard JSON string.

Add middleware

Middleware can perform pre/post unified logic on all requests, such as logging, permission verification, cross-domain settings, etc.:

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

Now each request will output the request path and final response code in the terminal, which is very convenient for debugging.


Production environment deployment suggestions

  1. Do not use it directlyweb.run_app()Online Its own server is only suitable for development and debugging, and its performance and stability are not enough to handle online traffic.

  2. Recommended combination: 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_appExpresses fromappmodule importmake_appFactory function.
    • GunicornUVLoopWebWorkerWill be used automaticallyuvloopReplacing the default event loop can improve performance by 2 to 4 times.
  • The number of workers is generally set to 2 times the number of CPU cores (adjusted based on the I/O intensity of the business).
  1. Reverse proxy Another layer of Nginx is hung in front, which is responsible for static file processing, SSL termination, load balancing and request buffering, allowing the Python process to focus on dynamic logic.

Summarize

Through this tutorial, you have masteredaiohttpCore usage on the server side:

  • How to define an asynchronous route and return an HTML/JSON response.
  • How to use middleware to handle common logic.
  • How to prepare deployment scenarios for production environments.

aiohttpIts capabilities go far beyond that, it also has built-in clients (can be used for asynchronous crawlers), WebSocket support, and rich third-party extensions (such as template engines, Session management). If your project requires lightweight, efficient, asynchronous-first web services,aiohttpWould be an excellent choice.