[ Bài đăng này được cập nhật kể từ 2012-09 / 02 (mới hơn ở trên). ]
Node.js hoàn toàn không mở rộng quy mô trên các máy đa lõi.
Có, Node.js là một luồng trên mỗi tiến trình. Đây là một quyết định thiết kế rất có chủ ý và loại bỏ sự cần thiết phải giải quyết các ngữ nghĩa khóa. Nếu bạn không đồng ý với điều này, có lẽ bạn chưa nhận ra việc gỡ lỗi mã đa luồng cực kỳ khó đến mức nào. Để giải thích sâu hơn về mô hình quy trình Node.js và lý do tại sao nó hoạt động theo cách này (và tại sao nó KHÔNG BAO GIỜ hỗ trợ nhiều luồng), hãy đọc bài đăng khác của tôi .
Vậy làm cách nào để tận dụng hộp 16 lõi của mình?
Hai lối:
- Đối với các tác vụ tính toán lớn như mã hóa hình ảnh, Node.js có thể kích hoạt các tiến trình con hoặc gửi tin nhắn đến các tiến trình công nhân bổ sung. Trong thiết kế này, bạn sẽ có một luồng quản lý luồng sự kiện và N tiến trình thực hiện các tác vụ tính toán nặng và nhai 15 CPU khác.
- Để mở rộng thông lượng trên một dịch vụ web, bạn nên chạy nhiều máy chủ Node.js trên một hộp, mỗi lõi và phân chia lưu lượng yêu cầu giữa chúng. Điều này cung cấp mối quan hệ CPU tuyệt vời và sẽ mở rộng thông lượng gần như tuyến tính với số lượng lõi.
Mở rộng thông lượng trên một dịch vụ web
Vì v6.0.X Node.js đã bao gồm mô-đun cụm ra khỏi hộp, điều này giúp dễ dàng thiết lập nhiều công nhân nút có thể nghe trên một cổng. Lưu ý rằng điều này KHÔNG giống như mô-đun "cụm" học cũ hơn có sẵn trong npm .
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
http.Server(function(req, res) { ... }).listen(8000);
}
Công nhân sẽ cạnh tranh để chấp nhận các kết nối mới, và quá trình tải ít nhất có khả năng giành chiến thắng nhất. Nó hoạt động khá tốt và có thể tăng quy mô thông lượng khá tốt trên hộp đa lõi.
Nếu bạn có đủ tải để quan tâm đến nhiều lõi, thì bạn cũng sẽ muốn làm thêm một vài điều nữa:
Chạy dịch vụ Node.js của bạn đằng sau một proxy web như Nginx hoặc Apache - thứ gì đó có thể điều chỉnh kết nối (trừ khi bạn muốn điều kiện quá tải để đưa hộp xuống hoàn toàn), viết lại URL, cung cấp nội dung tĩnh và proxy các dịch vụ phụ khác.
Định kỳ tái chế các quy trình công nhân của bạn. Đối với một quá trình dài, thậm chí rò rỉ bộ nhớ nhỏ cuối cùng sẽ tăng lên.
Thiết lập thu thập / giám sát nhật ký
Tái bút: Có một cuộc thảo luận giữa Aaron và Christopher trong các bình luận của một bài đăng khác (như bài viết này, bài viết hàng đầu của nó). Một vài bình luận về điều đó:
- Một mô hình ổ cắm được chia sẻ rất thuận tiện cho phép nhiều quá trình lắng nghe trên một cổng duy nhất và cạnh tranh để chấp nhận các kết nối mới. Về mặt khái niệm, bạn có thể nghĩ về Apache đã làm sẵn điều này với sự cảnh báo quan trọng rằng mỗi quá trình sẽ chỉ chấp nhận một kết nối duy nhất và sau đó chết. Mất hiệu quả đối với Apache là do chi phí cho các quy trình mới và không liên quan gì đến các hoạt động của ổ cắm.
- Đối với Node.js, có N công nhân cạnh tranh trên một ổ cắm là một giải pháp cực kỳ hợp lý. Cách khác là thiết lập giao diện người dùng trên hộp như Nginx và có lưu lượng proxy đó cho từng công nhân, xen kẽ giữa các công nhân để gán kết nối mới. Hai giải pháp có đặc điểm hiệu suất rất giống nhau. Và vì, như tôi đã đề cập ở trên, bạn có thể muốn có Nginx (hoặc một giải pháp thay thế) trước bất kỳ dịch vụ nút nào của bạn, sự lựa chọn ở đây thực sự nằm giữa:
Cổng chia sẻ: nginx (port 80) --> Node_workers x N (sharing port 3000 w/ Cluster)
đấu với
Cổng cá nhân: nginx (port 80) --> {Node_worker (port 3000), Node_worker (port 3001), Node_worker (port 3002), Node_worker (port 3003) ...}
Có thể có một số lợi ích cho việc thiết lập các cổng riêng lẻ (tiềm năng có ít khớp nối giữa các quy trình, có các quyết định cân bằng tải phức tạp hơn, v.v.), nhưng chắc chắn sẽ có nhiều công việc hơn để thiết lập và mô-đun cụm tích hợp là thấp thay thế linh hoạt mà làm việc cho hầu hết mọi người.