Tokio: runtime bất đồng bộ cho Rust
Tokio là runtime bất đồng bộ mà một dự án lớn hơn dựa vào — axum, hyper, và tonic là các crate đồng hành do cùng đội ngũ duy trì, không phải phần thêm sau. Scheduler work-stealing và reactor ở tầng hệ điều hành xứng đáng với sự tin tưởng đó: chúng gánh phần sổ sách đồng thời để từng crate không phải làm lại. Cái giá thật nằm ở chính Rust bất đồng bộ — ràng buộc `Send` và pinning khó suy luận hơn code chặn, và không runtime nào giấu được điều đó.
Tokio là gì?
Tokio là một thư viện Rust cung cấp runtime bất đồng bộ cho các ứng dụng nặng về I/O và mạng. Nó gồm một scheduler đa luồng theo cơ chế work-stealing, một reactor dựng trên hàng đợi sự kiện của hệ điều hành (epoll, kqueue, hoặc IOCP), và các kiểu socket TCP/UDP bất đồng bộ, giúp code `async fn` chạy mà không chặn luồng OS. Một chương trình khởi động runtime bằng macro `#[tokio::main]` và giao việc qua `tokio::spawn`.
Các thành phần và tính năng cốt lõi
- ✓Scheduler đa luồng theo cơ chế work-stealing, tự động phân chia các task đã spawn ra nhiều luồng OS
- ✓Reactor dựng trực tiếp trên hàng đợi sự kiện của hệ điều hành — epoll, kqueue, hoặc IOCP tùy nền tảng
- ✓Các kiểu socket TCP và UDP bất đồng bộ trong `tokio::net`, cùng timer cho việc trễ và tick theo chu kỳ
- ✓Macro `#[tokio::main]` và `#[tokio::test]` tự động kết nối runtime quanh code `async fn` thông thường
- ✓Hệ thống feature flag để Cargo.toml chỉ kéo vào phần cần dùng, hoặc dùng `features = ["full"]` để lấy tất cả cùng lúc
- ✓Dự án Tokio còn duy trì các crate đồng hành — axum, hyper, tonic, tower, tracing, mio, bytes, loom — phủ HTTP, gRPC, tracing, và kiểm thử concurrency, theo danh sách Related Projects trong README
Bạn có thể xây dựng gì với Tokio
- •Server và proxy TCP/UDP cần giữ mở hàng nghìn kết nối cùng lúc
- •Dịch vụ HTTP và gRPC xây trên các crate đồng hành axum, tonic, hoặc hyper
- •Công cụ CLI và worker chạy nền, kết hợp gọi mạng, timer, và I/O file mà không chặn một luồng cho mỗi request
- •Bất kỳ binary Rust nào có workload giống ví dụ `mini-redis` mà README trỏ tới — xử lý request/response đồng thời
Thêm Tokio vào dự án Rust
Thêm `tokio = { version = "1.53.1", features = ["full"] }` vào mục `[dependencies]` trong Cargo.toml — đúng theo snippet mà README đưa ra trong ví dụ của họ. Feature flag `full` bật toàn bộ các phân hệ của Tokio (networking, timer, I/O, sync, macro) cùng lúc; docs.rs liệt kê từng flag riêng nếu build gọn hơn quan trọng hơn sự tiện lợi.
Viết code async với Tokio
Ví dụ trong README là một TCP echo server: bind một `TcpListener` bằng `.await`, lặp trên `.accept().await`, rồi giao mỗi kết nối cho `tokio::spawn(async move { ... })` để nó chạy như một task riêng. Bên trong task đó, `AsyncReadExt`/`AsyncWriteExt` cung cấp các hàm `.read()` và `.write_all()` có thể await thay vì chặn luồng. Toàn bộ nằm dưới điểm vào `#[tokio::main] async fn main()`, chính là thứ khởi động runtime. Đó là toàn bộ pattern.
Điểm mạnh
- ✓Scheduler tự xử lý backpressure và cancellation — bạn không phải tự viết thread pool hay giới hạn kết nối
- ✓Dự án Tokio duy trì các crate đồng hành tương ứng cho HTTP (hyper), web (axum), và gRPC (tonic), nên một dịch vụ hướng mạng có thể lấy các phần từ cùng một dự án thay vì ghép nhiều thư viện không liên quan
- ✓MSRV và các bản LTS được ghi rõ trong README với số phiên bản và thời hạn hỗ trợ cụ thể, không chỉ là một lời hứa suông về sự ổn định
- ✓Ví dụ TCP echo server trong README đủ ngắn để dán thẳng vào một project Cargo mới và chạy ngay
Hạn chế và đánh đổi về độ phức tạp
- △Rust bất đồng bộ có đường cong học tập riêng ngoài ownership và borrowing — pinning, ràng buộc `Send` trên future được spawn, và ngữ nghĩa cancellation làm người mới bối rối
- △Chọn feature flag là một quyết định thật, không phải thủ tục hình thức; dùng `features = ["full"]` cho chắc ăn sẽ kéo vào nhiều hơn phần lớn binary cần
- △MSRV theo README là 1.71, nên các toolchain Rust cũ hơn không compile được
- △Trộn code sync và async — gọi I/O chặn bên trong một task — làm scheduler bị treo âm thầm thay vì báo lỗi
Các lựa chọn thay thế Tokio cho Rust
Câu hỏi thường gặp
Tokio đã sẵn sàng cho production ở các dịch vụ lưu lượng cao: các crate đồng hành như axum, hyper, và tonic — vốn dùng để triển khai dịch vụ HTTP và gRPC thật — được chính dự án này xây dựng, và Tokio duy trì các bản LTS có tài liệu rõ ràng với thời hạn hỗ trợ nhiều năm cho team cần một phiên bản cố định, được vá lỗi.
MSRV (phiên bản Rust tối thiểu được hỗ trợ) của Tokio là 1.71, theo README của dự án. README cũng liệt kê MSRV cho từng bản minor trước đó tính từ Tokio 1.0, và nêu rõ Tokio theo chính sách MSRV cuốn chiếu, chỉ nâng lên phiên bản Rust đã phát hành ít nhất sáu tháng.
Tokio ghép trực tiếp với Axum — Axum là một trong các crate của chính dự án Tokio, xây trên nền runtime của Tokio, nên hai thứ này dùng cùng nhau mặc định. README không nói rõ về việc tích hợp với Actix Web, nên với câu hỏi đó nên tra tài liệu riêng của Actix.
README của Tokio không đưa ra benchmark so sánh với async-std, nên một so sánh hiệu năng cụ thể không phải là điều tài liệu chính thức của dự án này nêu ra. Điều README có ghi là scheduler của chính Tokio: đa luồng, theo cơ chế work-stealing, đi cùng reactor dựa trên hàng đợi sự kiện hệ điều hành (epoll, kqueue, hoặc IOCP).
Ví dụ trong chính README bật `features = ["full"]` trong Cargo.toml, bật toàn bộ các phân hệ của Tokio cùng lúc. Để build gọn hơn, docs.rs liệt kê từng feature flag riêng lẻ, cho phép bạn chỉ bật đúng phần — networking, timer, macro, sync primitive — mà ứng dụng thực sự dùng tới.
Tokio có các bản phát hành LTS: một số phiên bản minor được chỉ định là LTS và nhận backport bugfix trong ít nhất một năm. Theo README, các dòng LTS hiện tại là 1.47.x, hỗ trợ tới tháng 9/2026, và 1.51.x, hỗ trợ tới tháng 3/2027.
Vấn đề mà nó giải quyết
Standard library của Rust chỉ có I/O chặn — một lệnh gọi `std::net::TcpListener` giữ một luồng OS đứng chờ tới khi có dữ liệu, nên một server muốn giữ mở hàng nghìn kết nối buộc phải tốn một luồng cho mỗi kết nối hoặc tự viết event loop riêng. Tokio tồn tại để code ở tầng source vẫn giữ được mô hình "một task cho mỗi kết nối", trong khi các luồng bên dưới thực sự được chia sẻ và work-stealing qua lại. Không có gì ép buộc điều đó theo mặc định.
Ai nên thử — và ai nên bỏ qua
Dùng Tokio khi một dự án Rust thực sự xử lý I/O mạng hoặc file đồng thời — một server xử lý nhiều kết nối, một proxy, một client RPC — vì đó chính là trường hợp mà scheduler và reactor của nó được thiết kế cho. Bỏ qua nó nếu đó chỉ là một script hay CLI tool chạy tuần tự các lệnh gọi chặn; thêm một runtime bất đồng bộ vào đó chỉ là thêm thủ tục (pinning, `.await` khắp nơi, một wrapper `#[tokio::main]`) cho một chương trình chẳng được lợi gì từ tính đồng thời.
Repo liên quan
Vẫn đang phân vân về tokio?
Một cú bấm sẽ gửi câu hỏi kèm trang này cho AI — xem AI nói gì về tokio.
