Tại sao Node.js đơn luồng? [đóng cửa]


255

Trong các máy chủ web dựa trên PHP (hoặc Java / ASP.NET / Ruby), mọi yêu cầu của máy khách đều được khởi tạo trên một luồng mới. Nhưng trong Node.js, tất cả các máy khách chạy trên cùng một luồng (chúng thậm chí có thể chia sẻ cùng một biến!) Tôi hiểu rằng các thao tác I / O dựa trên sự kiện để chúng không chặn vòng lặp luồng chính.

Điều tôi không hiểu là TẠI SAO tác giả của Node lại chọn nó là một luồng đơn? Nó làm cho mọi thứ khó khăn. Ví dụ: tôi không thể chạy chức năng chuyên sâu CPU vì nó chặn luồng chính (và các yêu cầu máy khách mới bị chặn) vì vậy tôi cần sinh ra một quy trình (có nghĩa là tôi cần tạo một tệp JavaScript riêng và thực hiện quy trình nút khác trên nó). Tuy nhiên, trong PHP cpu, các tác vụ chuyên sâu không chặn các máy khách khác vì như tôi đã đề cập, mỗi máy khách nằm trên một luồng khác nhau. Ưu điểm của nó so với các máy chủ web đa luồng là gì?

Lưu ý: Tôi đã sử dụng phân cụm để khắc phục điều này, nhưng nó không đẹp.


12
Gần đây tôi đã xem một video hay (29 phút) giải thích một số lý thuyết đằng sau Node. Tôi thậm chí nghĩ rằng anh chàng nói về các nhiệm vụ chuyên sâu của CPU và cách xử lý ngắn gọn: youtube.com/watch?v=L0pjVcIsU6A
whirlwin

24
Bạn có thể biết điều này, nhưng để rõ ràng Node.js không phải là một luồng đơn. Mã JavaScript của bạn chạy một luồng đơn, nhưng các hoạt động IO và những thứ khác mà các plugin có thể chạy ra khỏi một nhóm luồng. Node.js mang lại cho bạn nhiều lợi ích của đa luồng mà không phải xử lý mã đa luồng. Ngoài ra, những người đóng góp Node.js đã không chọn bản chất đơn luồng của JavaScript, các tác giả của JavaScript đã làm. Tôi không thể nghĩ ra cách mà JS có thể hoạt động trong bối cảnh đa luồng, nhưng ngay cả khi có, V8 cũng không được viết theo cách mà Node.js sử dụng làm công cụ JavaScript của nó.
Brad

5
PHP là một luồng đơn hơn JavaScript. Có lẽ bạn đang nghĩ về các mô-đun máy chủ như FastCGI hoặc mod_php. Vì vậy, trên thực tế, bạn đang so sánh Node.js với Apache, Nginx hoặc IIS, không phải với PHP, Java hoặc Ruby.
Álvaro González

34
Nút không phải là luồng đơn. Đó là một quan niệm sai lầm phổ biến. Thậm chí đơn giản node -e 'setTimeout(()=>{},1000);' & ps -T h $! | wc -l; kill $!hiển thị năm chủ đề trên hệ thống của tôi. Vòng lặp sự kiện chính là luồng đơn (sẽ không có ý nghĩa gì nếu không) nhưng Node rất đa luồng và bạn có thể viết các ứng dụng xử lý đơn đa luồng nếu muốn. Tôi rất thích viết một câu trả lời toàn diện về nó nhưng một số người đã quyết định đóng câu hỏi của bạn để tôi không thể. Tôi đang bỏ phiếu để mở lại nó. Nếu nó nhận được nhiều phiếu hơn và được mở lại thì hãy đề cập đến tôi trong phần bình luận.
rsp

2
@rsp cảm ơn bình luận của bạn, nhưng ý tôi là trong chủ đề chính không liên quan đến tôi. nếu bạn đang làm một cái gì đó liên quan đến cpu như một vòng lặp lớn để thực hiện điều gì đó thì máy chủ sẽ ngừng xử lý các kết nối. có nghĩa là, máy chủ không thể sử dụng tại thời điểm đó. Vì vậy, chúng tôi còn lại sử dụng các bản hack như cụm chỉ để làm một cái gì đó đơn giản thay vì nó vốn đã xâu chuỗi mọi kết nối như hầu hết các máy chủ làm. jxcore.com đã cố gắng giải quyết vấn đề này nhưng sau đó nó khiến người ta sử dụng các plugin nút đặc biệt / được sửa đổi, về cơ bản làm cho nó không thể sử dụng được với tôi.
foreyez

Câu trả lời:


292

Node.js được tạo rõ ràng như một thử nghiệm trong xử lý async. Lý thuyết là việc thực hiện xử lý không đồng bộ trên một luồng có thể cung cấp hiệu năng và khả năng mở rộng nhiều hơn theo tải web điển hình so với triển khai dựa trên luồng thông thường.

Và bạn biết những gì? Theo ý kiến ​​của tôi, lý thuyết đã được đưa ra. Một ứng dụng node.js không thực hiện các công cụ chuyên sâu về CPU có thể chạy hàng ngàn kết nối đồng thời hơn so với Apache hoặc IIS hoặc các máy chủ dựa trên luồng khác.

Bản chất đơn luồng, không đồng bộ làm cho mọi thứ phức tạp. Nhưng bạn có thành thật nghĩ rằng nó phức tạp hơn luồng không? Một điều kiện cuộc đua có thể làm hỏng cả tháng của bạn! Hoặc làm trống nhóm luồng của bạn do một số cài đặt ở đâu đó và xem thời gian phản hồi của bạn chậm để thu thập dữ liệu! Không đề cập đến các bế tắc, đảo ngược ưu tiên và tất cả các điều hướng khác đi với đa luồng.

Cuối cùng, tôi không nghĩ nó tốt hơn hay xấu hơn; nó khác biệt, và đôi khi nó tốt hơn và đôi khi không. Sử dụng các công cụ thích hợp cho công việc.


26
Nhưng các máy chủ web thường thực hiện ALOT các công cụ chuyên sâu về cpu, đó không phải là tìm nạp cơ sở dữ liệu. Chúng ta cần xử lý những gì chúng ta tìm nạp và thực hiện rất nhiều logic kinh doanh rất nhiều thời gian trước khi phục vụ nó cho khách hàng.
foreyez

22
Vì vậy, chỉ cần sinh sản công nhân, tốt! Đó là toàn bộ thỏa thuận với Node.js. Những thứ nặng có thể chạy trong một quy trình khác và bạn xử lý nó sẽ dẫn đến một cuộc gọi lại nhẹ.
MaiaVictor

7
Vấn đề với điều đó là có một quy trình cấp os chạy cho mỗi công nhân .. Bạn sẽ thấy họ sử dụng lệnh "ps". Vì vậy, điều đó có nghĩa là hàng ngàn tiến trình đang chạy trên máy cùng một lúc - đó là điều tồi tệ!
foreyez

9
@Foryez, Bạn không cần một quy trình cho mỗi người dùng. Bạn có sự lựa chọn trong cách bạn chia tải. Ngoài ra, không phải ai cũng làm được rất nhiều thứ chuyên sâu về CPU. Nút là một công cụ cho một công việc ... có thể không phải là công việc của bạn, nhưng nhiều loại công việc.
Brad

15
Trên thực tế, tôi muốn @Foryez sao lưu tuyên bố rằng "các máy chủ web thường dùng ALOT (sic) của các công cụ chuyên sâu về cpu". Theo kinh nghiệm của tôi, họ không. Hoặc có thể định nghĩa của tôi về 'cpu thâm' khác với anh ấy. Chuyển đổi dữ liệu sản phẩm thành UI không cần nhiều CPU, cũng không tính toán đơn hàng hoặc tương tự. Hầu hết các trang web là giao dịch khá. Công cụ chuyên sâu về CPU là những thứ như chuyển đổi video, chuyển đổi định dạng hình ảnh, v.v ... Phần lớn là do tập tin i / o mà trên thực tế, nút hoạt động khá tốt. Và làm cho nó dễ dàng giảm tải cho một quy trình khác dành riêng cho việc chuyển đổi.
Paul

62

Vấn đề với mô hình "một luồng trên mỗi yêu cầu" đối với máy chủ là chúng không mở rộng tốt cho một số tình huống so với mô hình luồng vòng lặp sự kiện.

Thông thường, trong các kịch bản chuyên sâu I / O, các yêu cầu dành phần lớn thời gian chờ đợi I / O hoàn thành. Trong thời gian này, trong mô hình "một luồng trên mỗi yêu cầu", các tài nguyên được liên kết với luồng (như bộ nhớ) không được sử dụng và bộ nhớ là yếu tố giới hạn. Trong mô hình vòng lặp sự kiện, luồng vòng lặp chọn sự kiện tiếp theo (kết thúc I / O) để xử lý. Vì vậy, các chủ đề luôn luôn bận rộn (tất nhiên nếu bạn lập trình nó chính xác).

Mô hình vòng lặp sự kiện vì tất cả những điều mới có vẻ sáng bóng và giải pháp cho tất cả các vấn đề nhưng mô hình nào sẽ sử dụng sẽ phụ thuộc vào kịch bản bạn cần giải quyết. Nếu bạn có một kịch bản I / O chuyên sâu (như proxy), mô hình cơ sở sự kiện sẽ cai trị, trong khi đó một kịch bản chuyên sâu về CPU với số lượng quy trình đồng thời thấp sẽ hoạt động tốt nhất với mô hình dựa trên luồng.

Trong thế giới thực, hầu hết các kịch bản sẽ có một chút ở giữa. Bạn sẽ cần phải cân bằng giữa nhu cầu thực sự về khả năng mở rộng với độ phức tạp phát triển để tìm kiến ​​trúc chính xác (ví dụ: mặt trước cơ sở sự kiện ủy nhiệm cho phụ trợ cho các tác vụ chuyên sâu của CPU. Mặt trước sẽ sử dụng ít tài nguyên chờ tác vụ kết quả.) Như với bất kỳ hệ thống phân tán nào, nó đòi hỏi một số nỗ lực để làm cho nó hoạt động.

Nếu bạn đang tìm kiếm viên đạn bạc phù hợp với bất kỳ kịch bản nào mà không cần bất kỳ nỗ lực nào, bạn sẽ kết thúc với một viên đạn trong chân mình.


8
Node.js bị hạn chế xử lý chỉ sự kiện do thiếu hỗ trợ đa luồng v8. Vâng, bản thân ngôn ngữ javascript thiếu các tính năng cần thiết, do đó, bất kỳ triển khai nào cũng sẽ trở nên khó khăn. Đó là thủ phạm chính của Node.js, theo ý kiến ​​của tôi. Trong các ngôn ngữ khác, bạn có thể chọn những gì bạn muốn. Hoặc một số lai của cả hai mô hình, như java NIO.
FrameGrace

2
@Kazaag, máy chủ web hiện đại làm duy trì một threadpool. Họ không chỉ sinh ra một chủ đề mới cho mỗi lần tải trang. Đó là những máy chủ web cũ hơn.
Pacerier

1
@Pacerier Tôi không bao giờ nói rằng một luồng mới được sinh ra, nhưng mỗi luồng được phân bổ cho một yêu cầu cho đến khi yêu cầu kết thúc.
Kazaag

2
@Kazaag Chắc chắn không phải là một quy tắc chung rằng "mỗi luồng được phân bổ cho một yêu cầu cho đến khi yêu cầu kết thúc". Tức là trong .Net (bao gồm xử lý các yêu cầu HTTP) người ta có thể và nên sử dụng lập trình async (Dựa trên tác vụ) và điều này sẽ giải phóng các luồng trong khi chờ I / O và các hoạt động async khác hoàn tất. Điều này cũng có thể áp dụng cho lập trình cấp cao, tức là bộ điều khiển MVC / API. Vì vậy, trong thực tế có thể có 20 yêu cầu HTTP đang chờ xử lý nhưng chỉ có một luồng hoạt động.
dùng3285954

29

Câu chuyện dài, nút rút ra từ V8, là luồng đơn nội bộ. Có nhiều cách để khắc phục các ràng buộc cho các tác vụ cần nhiều CPU.

Tại một thời điểm (0,7), các tác giả đã cố gắng giới thiệu các phân lập như một cách triển khai nhiều luồng tính toán, nhưng cuối cùng đã bị xóa: https://groups.google.com/forum/#!msg/nodejs/zLzuo292hX0/F7gqfUiKi2sJ


Bạn có thêm thông tin liên quan đến "sự cô lập" này không?
Pacerier
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.