Khi mới bắt đầu tìm hiểu Nodejs, có một câu hỏi phổ biến liên quan đến cơ chế hoạt động bên dưới Nodejs được đặt ra cho người mới bắt đầu:
Tại sao Nodejs lại chỉ dùng 1 thread và được kỳ vọng sẽ có hiệu năng cao hơn các nền tảng sử dụng đa luồng (multi-thread) ? Chẳng phải đa luồng thì sẽ xử lý cùng lúc được nhiều việc hơn sao.
Cùng tìm hiểu lý giải phù hợp cho câu hỏi này.
- Trước hết cùng tìm hiểu cách các ứng dụng đa luồng handle request:
123456789request ──> spawn thread└──> wait for database request└──> answer requestrequest ──> spawn thread└──> wait for database request└──> answer requestrequest ──> spawn thread└──> wait for database request└──> answer request
Mỗi khi có 1 request tới, app sẽ sinh ra 1 thread để xử lý request được. Thread này sẽ chạy logic code, gọi các I/O event (tương tác DB, file, network …), đợi kết quả, và trả về cho client. Vì mỗi request được handle bởi 1 request, kiến trúc này thường được gọi là one thread per request model. Hầu hết các webserver Java sử dụng mô hình này.Điểm yếu của mô hình này:
- Khi số lượng request tăng lên, số thread cũng tăng lên => ngốn RAM
- Mỗi thread khi handle request liên quan đến I/O, trong khi đợi kết quả từ I/O thì ở trong trạng thái rảnh rỗi => lãng phí tài nguyên.
- Mô hình single-threaded
123456request ──> make database requestrequest ──> make database requestrequest ──> make database requestdatabase request complete ──> send responsedatabase request complete ──> send responsedatabase request complete ──> send responseTrong mô hình này, khi nhận request, main-thread sẽ xử lý và gọi các I/O event, sau đó tiếp tục nhận các request khác mà không chờ đợi kết quả từ I/O event => Chúng ta tránh được các điểm yếu của mô hình one thread per request.
- Trở lại với câu hỏi ở đầu bài, ta có thể nhận thấy mặc dù Nodejs là single thread nhưng không phải mọi tác vụ đều được xử lý tại thread này. Ví dụ khi request cần gọi vào database, process riêng của database sẽ xử lý việc gọi vào database, và rõ ràng là process của database này là multi-thread. Nói cách khác, main-thread của Nodejs sẽ ủy quyền đặc tính multi-thread cho các process khác. Trong thực tế, mọi tác vụ bất đồng bộ đều được main-thread ủy quyền cho các thread của process khác hoặc thread trong thread-pool (gồm mặc định 4 threads) của libuv.
Từ luận điểm trên ta có thể suy ra điểm yếu của mô hình single-thread, đó là các tác vụ đồng bộ cần nhiều CPU (Fourier transform, 3D rendering) đều được xử lý ở main-thread => nó sẽ busy và không thể nhân thêm request mới => block cả app. Ngoài ra nếu máy tính có nhiều core thì single thread cũng chỉ tận dụng được 1 core.