Protocol và Transport trong asyncio
asyncio cung cấp hai API ở hai mức trừu tượng cho lập trình mạng: streams (high-level, async/await thuần) và protocols/transports (low-level, callback-based). Cặp Protocol–Transport tách rõ trách nhiệm theo mô hình OSI — Transport lo việc truyền byte qua socket (tương đương tầng 4), Protocol lo ngữ nghĩa giao thức ở tầng ứng dụng (tầng 7). Tách biệt này cho phép cùng một Protocol chạy trên nhiều loại transport (TCP, SSL, pipe, subprocess stdio) mà không sửa code, và ngược lại cùng một transport phục vụ nhiều protocol khác nhau.
flowchart TB
L["asyncio Event loop"] --> T["Transport - tầng 4<br/>(TCP, SSL, pipe, subprocess)"]
T --> P["Protocol - tầng 7<br/>(HTTP, WebSocket, gRPC...)"]
P -->|on data| H["Application logic"]
H -->|write/close| T
T --> S[("OS socket / fd")]
Hợp đồng giữa Transport và Protocol
Transport đẩy dữ liệu vào Protocol qua callback, và nhận lệnh ghi từ Protocol qua phương thức. Bốn callback chính của asyncio.Protocol:
connection_made(transport)được gọi khi kết nối thiết lập, Protocol lưu tham chiếu Transportdata_received(data: bytes)được gọi mỗi khi Transport có byte mới đọc từ socketconnection_lost(exc)được gọi khi kết nối đóng (do client hoặc lỗi)eof_received()được gọi khi client gửi EOF half-close mà chưa đóng hẳn
Hai callback bổ sung cho flow control là pause_writing() và resume_writing() — Transport gọi khi buffer của OS chạm high/low water mark. Application phải tôn trọng để giữ bộ nhớ không phình; chi tiết xem backpressure ở tầng transport.
Phía Protocol gọi vào Transport bằng transport.write(data), transport.close(), transport.get_extra_info("socket") để truy cập metadata. Không có ngược lại — Protocol không tự đọc từ Transport, mà nhận data qua data_received.
class EchoProtocol(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
def data_received(self, data):
self.transport.write(data) # echo
def connection_lost(self, exc):
...
server = await loop.create_server(
protocol_factory=EchoProtocol,
sock=sock,
)
protocol_factory là một callable trả về Protocol instance mới cho mỗi connection — event loop tự gọi mỗi khi accept() thành công. Pattern này tránh state chia sẻ giữa các connection.
Vì sao tách Protocol và Transport
Trước asyncio, mô hình tương tự xuất hiện ở Twisted (giai đoạn 2002+) — Glyph Lefkowitz coi tách biệt này là một trong những bài học quan trọng nhất về thiết kế thư viện mạng. Lợi ích cụ thể:
- Reuse:
H11Protocolcài đặt một lần, chạy được cả qua plain TCP transport và SSL transport mà không sửa - Test: có thể mock Transport bằng object có
writeghi vào bytearray, test Protocol mà không cần socket thật - Composability: cùng một protocol đặt sau proxy, tunnel, hay subprocess pipe đều hoạt động
- Flow control: tách rõ trách nhiệm — Transport chịu áp lực kernel buffer, Protocol chịu áp lực application logic
Khi nào dùng streams thay vì Protocol/Transport
asyncio streams (asyncio.start_server, StreamReader, StreamWriter) là wrapper async/await trên Protocol/Transport. Dùng streams khi:
- giao thức đơn giản, đọc theo dòng hoặc theo prefix length
- ưu tiên code straight-line dễ đọc
- không cần kiểm soát fine-grained flow control
Dùng trực tiếp Protocol/Transport khi:
- viết web server hiệu năng cao (uvicorn, hypercorn, uasgi đều dùng cách này)
- cài đặt giao thức nhị phân phức tạp (HTTP/2, gRPC) cần parse stateful
- cần callback dạng push thay vì pull (Protocol nhận data ngay khi đến)
- cần override
pause_writing/resume_writingđể custom backpressure
Dẫn chứng từ uasgi
uasgi cài đặt H11Protocol (HTTP/1.1) và H2Protocol (HTTP/2) đều là subclass của asyncio.Protocol. data_received của H11Protocol feed bytes vào httptools parser; parser emit callback on_message_begin, on_header, on_headers_complete, on_body, on_message_complete cho protocol xử lý. Bytes ngược lại được ghi qua transport.write đã lưu từ connection_made. Đây là minh hoạ trực tiếp pattern hợp đồng giữa Transport và Protocol.
Nguồn tham khảo
- asyncio Transports and Protocols - Python documentation
- asyncio Streams - Python documentation
- Twisted Protocol and Transport - lịch sử của mô hình
- Source uASGI - H11Protocol
- Source uASGI - Server tạo Protocol qua protocol_factory
- Viết HTTP/2 server tương thích ASGI trong Python - blog cá nhân, giải thích Protocol/Transport theo OSI
Liên kết tri thức
- Event loop trong Python - event loop là cái điều phối callback của Protocol và quản lý vòng đời Transport
- Coroutine trong Python - Protocol callback có thể spawn coroutine để xử lý request bằng async/await
- Backpressure ở tầng transport trong asyncio - pause_writing và resume_writing là một mặt của hợp đồng Protocol/Transport
- Arbiter và worker trong ASGI server - mỗi worker chạy event loop điều phối nhiều Protocol instance qua protocol_factory
- HTTP parser dạng máy trạng thái - parser được gọi từ Protocol.data_received, tách parsing khỏi I/O
- Hội nhập ecosystem qua interface có sẵn - Protocol/Transport là interface chuẩn của asyncio mà mọi network code Python nên tuân thủ
Tags
Cập nhật: 2026-05-29