Sự thật về Async PHP: Fibers, epoll và PHP 8.6
Tôi đã làm việc với Laravel trong nhiều năm. Tôi sử dụng PHP đồng bộ (sync). Một yêu cầu gửi đến, tiến trình chạy, và phản hồi được gửi đi. Tôi chưa bao giờ cần đến mã bất đồng bộ (async).
Sau đó, tôi đọc về Polling API mới của PHP 8.6. Nó đã thay đổi hoàn toàn cách tôi nhìn nhận mọi thứ.
Dưới đây là những gì tôi đã học được về cách async hoạt động ở bên dưới lớp vỏ.
Vấn đề I/O
Khi bạn gọi một API, tiến trình PHP của bạn sẽ phải chờ.
Ví dụ:
$response = Http::get('https://api.example.com');
Nếu lệnh gọi đó mất 300ms, CPU của bạn sẽ không làm gì trong suốt 300ms đó. Nó ở trong trạng thái nghỉ (sleep). Đây chính là I/O chặn (blocking I/O).
Nếu bạn có ba lệnh gọi API:
- API A: 300ms
- API B: 400ms
- API C: 200ms
Tổng thời gian tuần tự: 900ms. Tổng thời gian async: 400ms (thời gian của lệnh gọi chậm nhất).
Async cho phép tiến trình của bạn thực hiện các công việc khác trong khi chờ đợi dữ liệu.
Select so với epoll
Để thực hiện async, bạn cần biết socket nào đã có sẵn dữ liệu.
select() PHP đã sử dụng
stream_select()từ phiên bản 4. Nó hoạt động bằng cách yêu cầu kernel theo dõi một danh sách các socket. Vấn đề là: Mỗi khi có dữ liệu đến, bạn phải quét lại toàn bộ danh sách. Đây gọi là "thuế quét lại" (rescan tax). Nó cũng có giới hạn khoảng 1024 kết nối.epoll (Linux) / kqueue (macOS) Đây là các tính năng của kernel. Thay vì quét một danh sách, kernel duy trì một danh sách sẵn sàng (ready-list). Nó chỉ cho bạn biết chính xác socket nào đã sẵn sàng. Điều này có thể mở rộng lên hàng nghìn kết nối mà không tốn thêm công sức.
epoll không phải là một tính năng của PHP. Nó là một tính năng của Linux. Go, Rust và Node.js đều sử dụng nó.
Fibers: Nút tạm dừng
PHP 8.1 đã giới thiệu Fibers. Tôi từng nghĩ Fibers sẽ tự thức tỉnh. Nhưng không phải vậy.
Một Fiber giống như một video đang bị tạm dừng. Nó sẽ ở trạng thái tạm dừng cho đến khi có ai đó gọi $fiber->resume().
Một Event Loop chỉ đơn giản là một đoạn mã PHP quyết định khi nào cần gọi resume().
Async I/O yêu cầu ba phần:
- Tạm dừng: Fibers (lõi PHP 8.1)
- Quyết định: Event Loop (mã PHP thuần)
- Biết: Kernel Polling (epoll/kqueue)
Trước PHP 8.6, PHP đã có phần "Tạm dừng" và "Quyết định", nhưng phần "Biết" lại phụ thuộc vào select() cũ hoặc các tiện ích mở rộng (extensions) bằng C chậm chạp.
PHP 8.6 đã lấp đầy khoảng trống này. Nó đưa một Polling API nguyên bản vào lõi. Giờ đây, PHP có thể sử dụng epoll hoặc kqueue trực tiếp mà không cần thêm các extension.
Bài học rút ra
Nếu bạn đang sử dụng Laravel với PHP-FPM, bạn chưa cần phải thay đổi bất cứ điều gì ngay lúc này.
Nhưng hãy hiểu điều này: Async không phải là phép màu. Nó chỉ là một cách thông minh để quản lý thời gian chờ đợi.
Đừng chỉ dừng lại ở việc tiêu thụ mã nguồn. Hãy chạy một script đơn giản. Làm cho nó lỗi. Đó mới là cách bạn thực sự học hỏi.
