Node.js trên các máy đa lõi


606

Node.js có vẻ thú vị, NHƯNG tôi phải bỏ lỡ điều gì đó - không phải Node.js chỉ điều chỉnh để chạy trên một tiến trình và luồng đơn?

Vậy thì làm thế nào để mở rộng quy mô cho CPU đa lõi và máy chủ đa CPU? Xét cho cùng, thật tuyệt vời khi tạo ra máy chủ một luồng nhanh nhất có thể, nhưng với tải trọng cao, tôi muốn sử dụng một số CPU. Và điều tương tự cũng làm cho các ứng dụng nhanh hơn - dường như ngày nay cách sử dụng nhiều CPU và song song hóa các tác vụ.

Node.js phù hợp với hình ảnh này như thế nào? Là ý tưởng của nó để phân phối nhiều trường hợp hoặc những gì?


4
Có vẻ như Ryah đang bắt đầu nghiêm túc về việc bao gồm hỗ trợ đa lõi tích hợp trong nút: github.com/joyent/node/commit/
Kẻ

2
Trình quản lý quy trình PM2 sử dụng mô đun cụm bên trong để truyền bá các ứng dụng NodeJS của bạn đến tất cả các lõi có sẵn: github.com/Unitech/pm2
Unitech

@broofa, Đó không phải là các chủ đề thực và các tiến trình con không có bộ nhớ chia sẻ. Đồng thời xem Nodejs tương đương với các biến luồng thực và biến động tĩnh của Java là gì? .
Pacerier

Câu trả lời:


697

[ 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:

  1. 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.

  2. Đị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.

  3. 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.


1
bạn có thể cung cấp bất kỳ lời khuyên nào để chạy các dịch vụ dựa trên nodejs khác nhau trên một hộp không? Ví dụ: Tôi có 1 máy chủ và muốn chạy myservice1.js trên CpuCore1 và myservice2.js trên CpuCore2. Tôi có thể sử dụng cụm cho việc này? hoặc nó chỉ hữu ích cho việc tạo các dịch vụ nhân bản?
UpTheCux

6
Bạn nên đăng một câu hỏi cho điều đó! (và tôi sẽ sao chép nhận xét này dưới dạng câu trả lời đầu tiên của bạn). Những gì bạn đang muốn làm là thực sự thực sự thực sự đơn giản. Bạn sẽ không thực sự cần "cụm", bạn chỉ cần chạy hai dịch vụ nút khác nhau. Hai kịch bản, hai quá trình, hai cổng. Ví dụ: bạn có thể có dịch vụ Nghe trên 3000 và dịch vụB nghe trên 3001. Mỗi dịch vụ đó có thể sử dụng "cụm" để có hơn 1 công nhân và tái chế chúng theo định kỳ, v.v. Sau đó, bạn có thể định cấu hình Nginx để nghe trên cổng 80 và chuyển tiếp tới dịch vụ chính xác dựa trên tiêu đề "Máy chủ" đến và / hoặc đường dẫn URL.
Dave Dopson

1
Cảm ơn. Tôi đã đăng một câu hỏi liên quan - bạn đã mô tả khá nhiều những gì tôi đã nghĩ, nhưng tôi không chắc về cách nhắm mục tiêu lõi CPU (khi sử dụng một cái gì đó như mãi mãi).
UpTheCux

Câu trả lời tuyệt vời ddopson. Cách tốt nhất để hai quá trình nút giao tiếp với nhau trên cùng một máy là gì? Có giao thức nào nhanh hơn TCP khi chúng ở trên cùng một máy không?
Winduptoy

1
@Serob_b - tốt, vâng. Chạy một ứng dụng Node.js trên nhiều máy là rất phổ biến. Không có thư viện cần thiết để làm như vậy. Bạn chỉ cần chạy mã của mình trên nhiều máy và phân phối tải giữa chúng. Kiến trúc phần mềm của bạn để nó mở rộng quy mô (nghĩa là nó lưu trữ trạng thái trong một số loại dịch vụ dữ liệu ngoài thay vì giữ trạng thái trong bộ nhớ) - đó là công việc của bạn.
Dave Dopson

45

Một phương pháp sẽ là chạy nhiều phiên bản của node.js trên máy chủ và sau đó đặt bộ cân bằng tải (tốt nhất là không chặn như nginx) trước chúng.


36
node.js cũng nhanh như nginx, bạn có thể đặt bộ cân bằng tải node.js trước các máy chủ của node.js nếu bạn cũng muốn :)
mikeal

26
ryan đặc biệt nói không làm điều này cho đến khi nút ổn định hơn. Cách tốt nhất là chạy nginx trước nút.
hồi sinh

2
đối với nginx trước nút, nó sẽ không giải quyết được một số vấn đề nhất định như nếu bạn có hàng đợi trong bộ nhớ. 2 trường hợp nút sẽ không thể truy cập vào hàng đợi của nhau.
hồi sinh

5
Đồng thời, nginx không hỗ trợ đầy đủ HTTP 1.1, vì vậy những thứ như WebSockets không thể được ủy quyền.
ashchristopher

2
@mikeal, resopollestion - Tôi mạnh về phía Nginx. Tôi đã gặp sự cố Node.js nhiều lần (không có stacktrace, chỉ chết). Tôi chưa bao giờ gặp Nginx. Nginx out-of-the-box được cấu hình với tất cả các loại tiết lưu lành mạnh. Node.js theo mặc định sẽ tiếp tục chấp nhận các kết nối mới theo sở thích để phục vụ các kết nối hiện có cho đến khi hộp bị hỏng ... vâng, toàn bộ hộp; Tôi đã đánh sập kernel trên hộp CentOS5 bằng cách kiểm tra căng thẳng Node (bây giờ điều đó thực sự không nên xảy ra). Tôi đã đến một chút và tôi thấy một tương lai tươi sáng cho Node, có khả năng bao gồm các vai trò loại LB chuyên dụng. Chỉ là chưa.
Dave Dopson

30

Ryan Dahl trả lời câu hỏi này trong buổi nói chuyện về công nghệ mà ông đã đưa ra tại Google vào mùa hè năm ngoái. Để diễn giải, "chỉ cần chạy nhiều quy trình nút và sử dụng một cái gì đó hợp lý để cho phép chúng giao tiếp. Ví dụ: IPC kiểu sendmsg () hoặc RPC truyền thống".

Nếu bạn muốn bị bẩn tay ngay lập tức, hãy kiểm tra mô-đun spark2 Forever . Nó làm cho sinh sản nhiều quá trình nút dễ dàng. Nó xử lý thiết lập chia sẻ cổng, do đó mỗi bên có thể chấp nhận các kết nối đến cùng một cổng và cũng có thể tự động trả lời lại nếu bạn muốn đảm bảo một quy trình được khởi động lại nếu / khi nó chết.

CẬP NHẬT - 10/11/11 : Đồng thuận trong cộng đồng nút dường như là Cluster hiện là mô-đun ưa thích để quản lý nhiều phiên bản nút trên mỗi máy. Mãi mãi cũng đáng xem.


8
Mãi mãi và Cluster làm những việc rất khác nhau. Bạn thậm chí có thể sử dụng cả hai. Mãi mãi khởi động lại một quá trình khi nó chết. Cụm quản lý nhiều công nhân. Bạn sẽ sử dụng Mãi mãi để quản lý quy trình tổng thể của mình ...
Dave Dopson

4
Ngoài ra, mô-đun learnboost phần lớn được thay thế bởi phiên bản Cluster được nướng vào Node v0.6.x (cảnh báo: bề mặt API khác nhau)
Dave Dopson

@broofa IPC mặc định được so sánh như thế nào khi cho phép sử dụng Redis hoặc Memcache, chỉ cần gửi chuỗi / dữ liệu / mảng ở giữa các quy trình? Cách nào sẽ nhanh hơn?
NiCk Newman

1
@broofa, IPC có tổng phí rất lớn so với bộ nhớ chia sẻ thực mà Java và C có khả năng thực hiện.
Pacerier

@Pacerier Đúng, nhưng bộ nhớ dùng chung chỉ giải quyết vấn đề làm thế nào để chia tỷ lệ trong ngữ cảnh của một máy chủ duy nhất, mà không giải quyết các vấn đề vĩ mô cần thiết để mở rộng trên nhiều máy chủ. Tức là làm thế nào để chạy trong đám mây.
broalid

20

Bạn có thể sử dụng mô-đun cụm . Kiểm tra này .

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {
    // Workers can share any TCP connection
    // In this case its a HTTP server
    http.createServer(function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
}

13

Đa nút khai thác tất cả các lõi mà bạn có thể có.
Hãy xem http://github.com/kriszyp/multi-node .

Đối với các nhu cầu đơn giản hơn, bạn có thể khởi động nhiều bản sao của nút trên các số cổng khác nhau và đặt bộ cân bằng tải trước chúng.


12

Node Js đang hỗ trợ phân cụm để tận dụng tối đa lợi thế của cpu của bạn. Nếu bạn không chạy nó với cluster, thì có lẽ bạn đang lãng phí khả năng phần cứng của mình.

Phân cụm trong Node.js cho phép bạn tạo các quy trình riêng biệt có thể chia sẻ cùng một cổng máy chủ. Ví dụ: nếu chúng tôi chạy một máy chủ HTTP trên Cổng 3000, thì đó là một Máy chủ chạy trên một luồng trên lõi đơn của bộ xử lý.

Mã hiển thị dưới đây cho phép bạn phân cụm ứng dụng của bạn. Mã này là mã chính thức được đại diện bởi Node.js.

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        console.log("I am running with ID : " + cluster.workers[id].process.pid);
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {

    //Do further processing.
}

kiểm tra bài viết này để xem hướng dẫn đầy đủ


11

Như đã đề cập ở trên, Cluster sẽ mở rộng và cân bằng tải ứng dụng của bạn trên tất cả các lõi.

thêm một cái gì đó như

cluster.on('exit', function () {
  cluster.fork();
});

Sẽ khởi động lại bất kỳ công nhân thất bại.

Ngày nay, rất nhiều người cũng thích PM2 , xử lý phân cụm cho bạn và cũng cung cấp một số tính năng giám sát thú vị .

Sau đó, thêm Nginx hoặc HAProxy vào trước một số máy đang chạy với phân cụm và bạn có nhiều cấp độ chuyển đổi dự phòng và khả năng tải cao hơn nhiều.


3
PM2 là tuyệt vời cho sử dụng sản xuất. Các công cụ giám sát đã giúp tôi giải quyết các vấn đề về bộ nhớ với các ứng dụng.
mbokil 15/03/2016

7

Phiên bản tương lai của nút sẽ cho phép bạn rẽ nhánh một quy trình và chuyển các thông báo đến nó và Ryan đã tuyên bố rằng anh ta muốn tìm cách nào đó để chia sẻ các trình xử lý tệp, vì vậy đây sẽ không phải là một triển khai Web Worker.

Tại thời điểm này không có một giải pháp dễ dàng nào cho việc này nhưng vẫn còn rất sớm và nút là một trong những dự án nguồn mở di chuyển nhanh nhất mà tôi từng thấy vì vậy mong đợi một điều gì đó tuyệt vời trong tương lai gần.


7

Spark2 dựa trên Spark hiện không còn được duy trì. Cluster là sự kế thừa của nó và nó có một số tính năng thú vị, như sinh ra một quy trình công nhân trên mỗi lõi CPU và đối phó với các công nhân đã chết.


Câu hỏi ban đầu và rất nhiều câu trả lời trong số này đã được vài tháng và với nút di chuyển rất nhanh, tôi đánh giá cao việc bạn thêm phần giới thiệu về Cluster. Sau khi xem Cluster và các ví dụ của nó, nó trông giống hệt những gì tôi (hoặc OP?) Muốn cho Node, cảm ơn!
Riyad Kalla

5

Tôi đang sử dụng Node worker để chạy các tiến trình một cách đơn giản từ quy trình chính của tôi. Có vẻ như đang làm việc tuyệt vời trong khi chúng tôi chờ đợi cách chính thức để đi xung quanh.


1
tại sao nút worker example.js không thể chạy, nút của tôi là phiên bản trước 0.3.3
guilin

5

Đứa trẻ mới trong khối ở đây là "Up" của LearnBoost .

Nó cung cấp "Tải lại không thời gian chết" và cũng tạo ra nhiều công nhân (theo mặc định số lượng CPU, nhưng nó có thể định cấu hình) để cung cấp tốt nhất trong tất cả các Thế giới.

Nó là mới, nhưng dường như khá ổn định, và tôi đang sử dụng nó một cách hạnh phúc trong một trong những dự án hiện tại của tôi.


5

Các cụm mô-đun cho phép bạn sử dụng tất cả các lõi của máy tính của bạn. Trong thực tế, bạn có thể tận dụng điều này chỉ trong 2 lệnh và không cần chạm vào mã của mình bằng trình quản lý quy trình rất phổ biến pm2 .

npm i -g pm2
pm2 start app.js -i max

4

Bạn có thể chạy ứng dụng node.js của mình trên nhiều lõi bằng cách sử dụng mô đun cụm kết hợp với os mô đun có thể được sử dụng để phát hiện số lượng CPU bạn có.

Ví dụ: hãy tưởng tượng rằng bạn có một servermô-đun chạy máy chủ http đơn giản trên phụ trợ và bạn muốn chạy nó cho một số CPU:

// Dependencies.
const server = require('./lib/server'); // This is our custom server module.
const cluster = require('cluster');
const os = require('os');

 // If we're on the master thread start the forks.
if (cluster.isMaster) {
  // Fork the process.
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // If we're not on the master thread start the server.
  server.init();
}


0

Bạn cũng có thể thiết kế dịch vụ web dưới dạng một số máy chủ độc lập lắng nghe các ổ cắm unix, để bạn có thể đẩy các chức năng như xử lý dữ liệu vào các quy trình riêng biệt.

Điều này tương tự với hầu hết các kiến ​​trúc máy chủ web cơ sở dữ liệu / cơ sở dữ liệu trong đó một quy trình cgi xử lý logic nghiệp vụ và sau đó đẩy và kéo dữ liệu qua ổ cắm unix vào cơ sở dữ liệu.

sự khác biệt là việc xử lý dữ liệu được viết dưới dạng một máy chủ web nút nghe trên một cổng.

nó phức tạp hơn nhưng cuối cùng là nơi phát triển đa lõi. một kiến ​​trúc đa xử lý sử dụng nhiều thành phần cho mỗi yêu cầu web.


0

Có thể chia tỷ lệ NodeJS ra nhiều hộp bằng cách sử dụng bộ cân bằng tải TCP thuần túy (HAProxy) trước nhiều hộp chạy một quy trình NodeJS mỗi hộp.

Nếu sau đó bạn có một số kiến ​​thức phổ biến để chia sẻ giữa tất cả các trường hợp, bạn có thể sử dụng cửa hàng Redis trung tâm hoặc tương tự có thể được truy cập từ tất cả các phiên bản quy trình (ví dụ: từ tất cả các hộp)


Trừ khi bạn có CPU lõi đơn trong các máy chủ đó, điều đó sẽ không sử dụng hết dung lượng CPU của bạn (trừ khi bạn cũng đang làm gì đó khác).
UpTheCux
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.