1. Kiến thức cơ bản về Hệ điều hành (OS)
Tóm tắt vài khái niệm sau:
- Kernel quản lý phần cứng, các tác vụ muốn sử dụng đều phải gọi như call API hay đọc ghi file, ngoài ra còn quản lý process, memory, I/O.
- Userland là nơi code php (hoặc các ngôn ngữ lập trình khác) được thực thi, không trực tiếp truy cập phần cứng, phải dùng system call (syscall) nhờ Kernel làm giúp.
- Zend VM đóng vai trò cầu nối giữa Userland và Kernel.
- Process là tiến trình xử lý code trên CPU, có PID (Process ID) riêng, xem được trên OS. Hai Process không share memory cho nhau.
- Thread là đơn vị nhỏ nhất để thực thi code, được chạy trong Process. Một Process có nhiều Thread, share memory cho nhau.
2. Concurrency, Parallel và Coroutine là gì?
- Concurrency dịch sang tiếng việt là Đồng Thời, tức là làm nhiều việc, nhưng không nhất thiết là tại một thời điểm, chạy trên một CPU.
Nghĩa là xử lý nhiều tác vụ trong cùng một giai đoạn thời gian, nhưng không nhất thiết là cùng một lúc. Chỉ một CPU core cũng có thể làm Concurrency thông qua việc chuyển đổi ngữ cảnh (context switching) rất nhanh. - Parallel dịch sang tiếng việt là Song Song, tức là làm nhiều việc cùng lúc vào một thời điểm, chạy trên nhiều core CPU.
Nghĩa là thực sự thực hiện nhiều tác vụ cùng lúc, trên nhiều core CPU. Mỗi core xử lý một công việc riêng biệt, nên không cần chuyển qua lại. - Coroutine dịch sang tiếng Việt gần nghĩa là Đồng Trình (hợp tác luồng).
Đây là kỹ thuật cho phép nhiều tác vụ tự nguyện nhường quyền điều khiển cho nhau mà không cần hệ điều hành can thiệp. Nó không chạy song song, nhưng cực kỳ nhẹ và hiệu quả, thường dùng trong async I/O như trong Swoole (một thư viện của PHP), Golang, hoặc JavaScript event-loop
Lấy ví dụ cho dễ hiểu:
- Một người đang nấu ăn, sau đó họ có cuộc gọi điện thoại, họ tắt nồi canh, họ nghe máy, rồi nấu ăn tiếp. Đây là Concurrency.
- Cũng người đó, nhưng vừa nấu ăn vừa nghe nhạc, thì đó là Parallel.
- Cũng người đó, đang nấu canh thì có cuộc gọi điện thoại, họ để đó, chờ khi nào đang nấu nước sôi, mới nghe máy, sau đó khi nào nước sôi thì nói: xíu tôi gọi lại. Rồi nấu canh tiếp, sau đó họ mới gọi lại người kia. Đây là Coroutine.
Kết luận:
- Concurrency diễn ra ở Kernel, phải switch context liên tục trên 1 CPU để xử lý đồng thời nên rất tốn kém (ghi nhớ mọi thứ về memory trước khi switch). Trong ví dụ trên, người đó tắt nồi canh để nghe điện thoại (giả sử là 5 phút), nồi canh đã nguội nên phải nấu lại.
- Parallel diễn ra ở Kernel, phải có nhiều Core CPU mới chạy song song thực sự được. Kernel phân bổ từng Thread lên từng Core. Diễn ra ở Kernel nên chi phí cao (chi phí về hạ tầng, hiệu năng phần cứng, hiệu năng xài bao nhiêu % CPU).
- Coroutine diễn ra hoàn toàn ở Userland, các tác vụ tự nhường nhau bằng pause, resume, không cần Kernel quyết định nên rất nhẹ, do code lập trình viên quy định.
3. PHP Runtime Mode
PHP có hai runtime mode chính tùy thuộc vào SAPI (Server API) mà nó chạy bên dưới:
- CLI Runtime Mode (Command-line execution)
- HTTP Runtime Mode (Web execution)
Hai mode này chia sẻ cùng Zend Engine, nhưng vòng đời thực thi (execution model) hoàn toàn khác nhau.
a. CLI Runtime Mode
Trước tiên nói về CLI mode, cách chạy:
php script.php
Sơ đồ:
start → run script sequentially → exit (unless long-running)
Mỗi Process PHP là một Process Userland độc lập, có main Thread duy nhất, là Long-Running, có thể chạy hàng giờ, không bị ép exit.
b. HTTP Runtime Mode
Có 2 cách để chạy HTTP, đầu tiên là sử dụng PHP-FPM:
Client → Nginx → FastCGI → PHP-FPM worker → Response
- PHP-FPM xem mỗi request như một chương trình PHP độc lập.
- Sau khi trả response, toàn bộ biến, object, container của request biến mất.
Đây là PHP Classic, khi PHP rất dễ deploy, an toàn, không sợ bị memory leak, tất cả các biến của chương trình, connection.. đều tự huỷ sau khi trả Response về. Tuy nhiên hiệu năng kém do phải khởi tạo lại mọi thứ khi nhận Request mới, kể cả Global Variable trong code php.
Thường dùng khi code app Server-Side Rendering (backend trả về html) hoặc khi dev Localhost.
Thứ hai là sử dụng Swoole:
Client → Nginx (reverse proxy)
→ Swoole server
→ persistent workers
Swoole sử dụng Coroutine nên sau khi trả Response thì các Global Variable vẫn được giữ lại. Do vậy hiệu năng tăng cao x10 lần.
Thường dùng làm API trên Production. Tuy nhiên điều này dễ gây memory leak nếu code không đúng cách.
Tóm lại:
HTTP Mode của PHP có hai runtime: PHP-FPM và Octane. PHP-FPM xử lý mỗi request với memory độc lập và bootstrap lại ứng dụng mỗi lần, còn Swoole giữ ứng dụng sống trong worker lâu dài, giảm chi phí khởi động và cho phép concurrency cấp userland thông qua coroutine. Cả hai đều đạt parallelism nhờ kernel scheduling trên nhiều worker process.