Cái nào sẽ tốt hơn cho các tác vụ đồng thời trên node.js? Sợi? Nhân viên web? hoặc Chủ đề?


111

Tôi đã tình cờ xem node.js cách đây một thời gian và thích nó rất nhiều. Nhưng ngay sau đó tôi phát hiện ra rằng nó thiếu khả năng thực hiện các tác vụ đòi hỏi nhiều CPU. Vì vậy, tôi bắt đầu googling và nhận được những câu trả lời này để giải quyết vấn đề: Fibers, Webworkers và Threads (thread-a-gogo). Bây giờ sử dụng cái nào là một sự nhầm lẫn và một trong số chúng chắc chắn cần phải được sử dụng - cho thấy mục đích của việc có một máy chủ chỉ tốt về IO và không có gì khác? Đề xuất cần thiết!

CẬP NHẬT:

Tôi đã nghĩ về một cách đi trễ; chỉ cần gợi ý về nó. Bây giờ, điều tôi nghĩ đến là: Hãy có một số chủ đề (sử dụng thread_a_gogo hoặc có thể là webworkers). Bây giờ, khi chúng ta cần thêm chúng, chúng ta có thể tạo thêm. Nhưng sẽ có một số giới hạn trong quá trình tạo. (không phải do hệ thống ngụ ý nhưng có thể là do chi phí). Bây giờ, khi vượt quá giới hạn, chúng ta có thể fork một nút mới và bắt đầu tạo các luồng trên đó. Bằng cách này, nó có thể tiếp tục cho đến khi chúng ta đạt đến một số giới hạn (xét cho cùng, các quy trình cũng có chi phí lớn). Khi đạt đến giới hạn này, chúng tôi bắt đầu các nhiệm vụ xếp hàng. Bất cứ khi nào một luồng trở nên rảnh rỗi, nó sẽ được giao một nhiệm vụ mới. Bằng cách này, nó có thể diễn ra suôn sẻ.

Vì vậy, đó là những gì tôi nghĩ đến. Ý tưởng này có tốt không? Tôi hơi mới đối với tất cả quy trình này và nội dung chuỗi, vì vậy không có bất kỳ chuyên môn nào về nó. Hãy chia sẻ ý kiến ​​của bạn.

Cảm ơn. :)


Xin lưu ý: Công nhân là một đặc điểm kỹ thuật của trình duyệt - không phải là một tính năng Javascript.
FredTheWebGuy

Tôi hiểu rồi. Câu hỏi của tôi là về node.js - mã máy chủ chứ không phải về phía máy khách!
Parth Thakkar

Chỉ cần làm rõ- Tôi thấy rằng câu hỏi ban đầu là về Webworkers trong NodeJs, điều này là không thể- NodeJs sử dụng "Threads". Tuy nhiên, có một mô-đun NodeJS nổi xung quanh cho phép cú pháp WebWorker trong thời gian chạy NodeJs.
FredTheWebGuy

Câu trả lời:


330

Node có một mô hình hoàn toàn khác và một khi nó được nắm bắt chính xác, sẽ dễ dàng thấy cách giải quyết vấn đề khác nhau này. Bạn không bao giờ cần nhiều luồng trong một ứng dụng Node (1) vì bạn có một cách khác để thực hiện cùng một việc. Bạn tạo nhiều quy trình; nhưng nó rất khác so với ví dụ như cách Prefork của Apache Web Server mpm hoạt động.

Bây giờ, hãy nghĩ rằng chúng ta chỉ có một lõi CPU và chúng ta sẽ phát triển một ứng dụng (theo cách của Node) để thực hiện một số công việc. Công việc của chúng tôi là xử lý một tệp lớn chạy trên từng byte nội dung của nó. Cách tốt nhất cho phần mềm của chúng tôi là bắt đầu công việc từ đầu tệp, theo từng byte cho đến cuối.

- Này, Hasan, tôi cho rằng bạn là học sinh mới hoặc rất cũ từ thời Ông của tôi !!! Tại sao bạn không tạo một số luồng và làm cho nó nhanh hơn nhiều?

- Ồ, chúng ta chỉ có một lõi CPU.

-- Vậy thì sao? Tạo một số người đàn ông chủ đề, làm cho nó nhanh hơn!

- Nó không hoạt động như vậy. Nếu tôi tạo chủ đề, tôi sẽ làm cho nó chậm hơn. Bởi vì tôi sẽ thêm rất nhiều chi phí vào hệ thống để chuyển đổi giữa các luồng, cố gắng cho chúng một khoảng thời gian vừa đủ và bên trong quy trình của tôi, cố gắng giao tiếp giữa các luồng này. Ngoài tất cả những sự kiện này, tôi cũng sẽ phải suy nghĩ về cách tôi sẽ chia một công việc thành nhiều phần có thể được thực hiện song song.

- Thôi được rồi, anh thấy em tội nghiệp lắm. Hãy sử dụng máy tính của tôi, nó có 32 lõi!

- Chà, bạn thật tuyệt, bạn thân mến của tôi, cảm ơn bạn rất nhiều. Tôi rât cảm kich!

Sau đó chúng tôi quay trở lại làm việc. Bây giờ chúng tôi có 32 lõi cpu nhờ người bạn giàu có của chúng tôi. Các quy tắc chúng ta phải tuân thủ vừa thay đổi. Bây giờ chúng tôi muốn sử dụng tất cả của cải chúng tôi được cho.

Để sử dụng nhiều lõi, chúng ta cần tìm cách chia công việc của mình thành các phần để chúng ta có thể xử lý song song. Nếu nó không phải là Node, chúng tôi sẽ sử dụng các luồng cho việc này; 32 luồng, một luồng cho mỗi lõi cpu. Tuy nhiên, vì chúng ta có Node, chúng ta sẽ tạo 32 Node process.

Các luồng có thể là một sự thay thế tốt cho các quy trình Node, thậm chí có thể là một cách tốt hơn; nhưng chỉ trong một loại công việc cụ thể mà công việc đã được xác định và chúng tôi có toàn quyền kiểm soát cách xử lý công việc đó. Ngoài ra, đối với mọi loại vấn đề khác mà công việc đến từ bên ngoài theo cách mà chúng tôi không kiểm soát được và chúng tôi muốn trả lời càng nhanh càng tốt, cách của Node là vượt trội hơn hẳn.

- Này, Hasan, bạn vẫn đang làm việc đơn luồng à? Anh bị làm sao vậy? Tôi vừa cung cấp cho bạn những gì bạn muốn. Bạn không có lời bào chữa nào nữa. Tạo chủ đề, làm cho nó chạy nhanh hơn.

- Tôi đã chia công việc thành nhiều phần và mọi quy trình sẽ thực hiện song song trên một trong những phần này.

- Tại sao bạn không tạo chủ đề?

- Xin lỗi, tôi nghĩ nó không sử dụng được. Bạn có thể lấy máy tính của bạn nếu bạn muốn?

- Không sao đâu, anh ngầu lắm, chỉ là không hiểu sao em không dùng đề thôi?

- Cảm ơn bạn về chiếc máy tính. :) Tôi đã chia công việc thành nhiều phần và tôi tạo các quy trình để làm việc trên các phần này song song. Tất cả các lõi CPU sẽ được sử dụng tối đa. Tôi có thể làm điều này với các chủ đề thay vì các quy trình; nhưng Node có cách này và sếp của tôi, Parth Thakkar muốn tôi sử dụng Node.

- Được rồi, hãy cho tôi biết nếu bạn cần một máy tính khác. : p

Nếu tôi tạo 33 quy trình, thay vì 32, bộ lập lịch của hệ điều hành sẽ tạm dừng một luồng, bắt đầu luồng kia, tạm dừng nó sau một số chu kỳ, bắt đầu lại luồng khác ... Đây là chi phí không cần thiết. Tôi không muốn nó. Trên thực tế, trên một hệ thống có 32 lõi, tôi thậm chí sẽ không muốn tạo chính xác 32 quy trình, 31 có thể đẹp hơn . Bởi vì nó không chỉ là ứng dụng của tôi sẽ hoạt động trên hệ thống này. Để lại một chút không gian cho những thứ khác có thể tốt, đặc biệt nếu chúng tôi có 32 phòng.

Tôi tin rằng chúng ta đang ở cùng một trang về việc sử dụng đầy đủ các bộ xử lý cho các tác vụ đòi hỏi nhiều CPU .

- Hmm, Hasan, tôi xin lỗi vì đã chế giễu cậu một chút. Tôi tin rằng tôi hiểu bạn hơn bây giờ. Nhưng vẫn còn điều gì đó mà tôi cần giải thích: Tất cả những gì đang xôn xao về việc chạy hàng trăm chủ đề? Tôi đọc ở khắp mọi nơi rằng các luồng nhanh hơn nhiều để tạo và ngu ngốc hơn nhiều so với các quy trình rèn? Bạn fork các quy trình thay vì các luồng và bạn nghĩ rằng đó là mức cao nhất bạn sẽ nhận được với Node. Vậy thì có phải Node không thích hợp cho loại công việc này không?

- Đừng lo, tôi cũng rất tuyệt. Mọi người đều nói những điều này nên tôi nghĩ tôi đã quen với việc nghe chúng.

-- Vì thế? Node không tốt cho điều này?

- Node hoàn toàn tốt cho việc này mặc dù các luồng cũng có thể tốt. Đối với chi phí tạo luồng / quy trình; về những thứ mà bạn lặp lại nhiều, mỗi mili giây đều có giá trị. Tuy nhiên, tôi chỉ tạo 32 quy trình và sẽ mất một khoảng thời gian nhỏ. Nó sẽ chỉ xảy ra một lần. Nó sẽ không tạo ra bất kỳ sự khác biệt nào.

- Khi nào tôi muốn tạo hàng nghìn chủ đề?

- Bạn không bao giờ muốn tạo hàng nghìn chủ đề. Tuy nhiên, trên một hệ thống đang thực hiện công việc đến từ bên ngoài, chẳng hạn như máy chủ web xử lý các yêu cầu HTTP; nếu bạn đang sử dụng một chủ đề cho mỗi yêu cầu, bạn sẽ tạo ra rất nhiều chủ đề, nhiều người trong số họ.

- Node thì khác? Đúng?

-- Đúng chính xác. Đây là nơi Node thực sự tỏa sáng. Giống như một luồng nhẹ hơn nhiều so với một quy trình, một lời gọi hàm nhẹ hơn nhiều so với một luồng. Node gọi các hàm, thay vì tạo luồng. Trong ví dụ về máy chủ web, mọi yêu cầu đến đều gây ra một lệnh gọi hàm.

- Hừ, thú vị; nhưng bạn chỉ có thể chạy một chức năng cùng lúc nếu bạn không sử dụng nhiều luồng. Làm thế nào điều này có thể hoạt động khi nhiều yêu cầu đến máy chủ web cùng một lúc?

- Bạn hoàn toàn đúng về cách chạy của các hàm, từng hàm một, không bao giờ là hai hàm song song. Ý tôi là trong một quy trình duy nhất, chỉ có một phạm vi mã đang chạy tại một thời điểm. Trình lập lịch hệ điều hành không đến và tạm dừng chức năng này và chuyển sang chức năng khác, trừ khi nó tạm dừng quy trình để dành thời gian cho quy trình khác, không phải luồng khác trong quy trình của chúng tôi. (2)

- Sau đó, làm thế nào một tiến trình có thể xử lý 2 yêu cầu cùng một lúc?

- Một tiến trình có thể xử lý hàng chục nghìn yêu cầu cùng một lúc miễn là hệ thống của chúng ta có đủ tài nguyên (RAM, Mạng, v.v.). Cách thức hoạt động của các chức năng đó là SỰ KHÁC BIỆT CHÍNH.

- Hừ, bây giờ có nên cao hứng không?

- Có thể :) Node chạy một vòng lặp qua hàng đợi. Trong hàng đợi này là các công việc của chúng tôi, tức là, các cuộc gọi mà chúng tôi bắt đầu xử lý các yêu cầu đến. Điểm quan trọng nhất ở đây là cách chúng ta thiết kế các hàm của mình để chạy. Thay vì bắt đầu xử lý một yêu cầu và bắt người gọi đợi cho đến khi chúng tôi hoàn thành công việc, chúng tôi nhanh chóng kết thúc chức năng của mình sau khi thực hiện một lượng công việc có thể chấp nhận được. Khi chúng ta đến một điểm mà chúng ta cần đợi một thành phần khác thực hiện một số công việc và trả về cho chúng ta một giá trị, thay vì chờ đợi điều đó, chúng ta chỉ cần hoàn thành hàm của mình thêm phần còn lại của công việc vào hàng đợi.

- Nghe phức tạp quá nhỉ?

- Không không, tôi nghe có vẻ phức tạp; nhưng bản thân hệ thống rất đơn giản và nó có ý nghĩa hoàn hảo.

Bây giờ tôi muốn ngừng trích dẫn cuộc đối thoại giữa hai nhà phát triển này và kết thúc câu trả lời của mình sau một ví dụ nhanh cuối cùng về cách các chức năng này hoạt động.

Bằng cách này, chúng tôi đang làm những gì OS Scheduler thường làm. Chúng tôi tạm dừng công việc của mình vào một thời điểm nào đó và để các lệnh gọi hàm khác (như các luồng khác trong môi trường đa luồng) chạy cho đến khi chúng tôi quay lại lượt. Điều này tốt hơn nhiều so với việc để công việc cho OS Scheduler, công cụ cố gắng cung cấp thời gian cho mọi luồng trên hệ thống. Chúng tôi biết những gì chúng tôi đang làm tốt hơn nhiều so với OS Scheduler và chúng tôi dự kiến ​​sẽ dừng lại khi chúng tôi nên dừng lại.

Dưới đây là một ví dụ đơn giản trong đó chúng tôi mở một tệp và đọc tệp đó để thực hiện một số thao tác trên dữ liệu.

Cách đồng bộ:

Open File
Repeat This:    
    Read Some
    Do the work

Cách không đồng bộ:

Open File and Do this when it is ready: // Our function returns
    Repeat this:
        Read Some and when it is ready: // Returns again
            Do some work

Như bạn thấy, chức năng của chúng tôi yêu cầu hệ thống mở một tệp và không đợi nó được mở. Nó tự kết thúc bằng cách cung cấp các bước tiếp theo sau khi tệp đã sẵn sàng. Khi chúng ta quay trở lại, Node sẽ chạy các lệnh gọi hàm khác trên hàng đợi. Sau khi chạy qua tất cả các chức năng, vòng lặp sự kiện chuyển sang lượt tiếp theo ...

Tóm lại, Node có một mô hình hoàn toàn khác so với phát triển đa luồng; nhưng điều này không có nghĩa là nó thiếu thứ. Đối với một công việc đồng bộ (nơi chúng ta có thể quyết định thứ tự và cách thức xử lý), nó hoạt động cũng như song song đa luồng. Đối với một công việc đến từ bên ngoài như các yêu cầu đến máy chủ, nó chỉ đơn giản là vượt trội.


(1) Trừ khi bạn đang xây dựng thư viện bằng các ngôn ngữ khác như C / C ++, trong trường hợp đó bạn vẫn không tạo luồng để phân chia công việc. Đối với loại công việc này, bạn có hai luồng, một trong số đó sẽ tiếp tục giao tiếp với Node trong khi luồng kia thực hiện công việc thực sự.

(2) Trên thực tế, mọi quy trình Node đều có nhiều luồng vì những lý do tương tự mà tôi đã đề cập trong chú thích đầu tiên. Tuy nhiên đây không phải là cách giống như 1000 chủ đề làm các công việc tương tự. Các chuỗi bổ sung đó dành cho những thứ như chấp nhận các sự kiện IO và xử lý thông báo giữa các quá trình.

CẬP NHẬT (Như trả lời một câu hỏi hay trong nhận xét)

@Mark, cảm ơn bạn vì những lời phê bình mang tính xây dựng. Trong mô hình của Node, bạn không bao giờ nên có các hàm mất quá nhiều thời gian để xử lý trừ khi tất cả các lệnh gọi khác trong hàng đợi được thiết kế để chạy lần lượt. Trong trường hợp các tác vụ tốn kém về mặt tính toán, nếu chúng ta nhìn vào bức tranh toàn cảnh, chúng ta thấy rằng đây không phải là câu hỏi "Chúng ta nên sử dụng luồng hay quy trình?" nhưng một câu hỏi đặt ra là "Làm thế nào chúng ta có thể phân chia các tác vụ này một cách cân bằng thành các tác vụ con mà chúng ta có thể chạy chúng song song bằng cách sử dụng nhiều lõi CPU trên hệ thống?" Giả sử chúng tôi sẽ xử lý 400 tệp video trên hệ thống có 8 lõi. Nếu chúng ta muốn xử lý từng tệp một, thì chúng ta cần một hệ thống xử lý các phần khác nhau của cùng một tệp, trong trường hợp đó, có thể, một hệ thống xử lý đơn đa luồng sẽ dễ xây dựng hơn và thậm chí hiệu quả hơn. Chúng ta vẫn có thể sử dụng Node cho việc này bằng cách chạy nhiều tiến trình và chuyển các thông báo giữa chúng khi cần chia sẻ / giao tiếp trạng thái. Như tôi đã nói trước đây, cách tiếp cận đa quy trình với Node làcũng như cách tiếp cận đa luồng trong loại nhiệm vụ này; nhưng không nhiều hơn thế. Một lần nữa, như tôi đã nói trước đây, tình huống mà Node tỏa sáng là khi chúng ta có các tác vụ này làm đầu vào cho hệ thống từ nhiều nguồn vì việc giữ nhiều kết nối đồng thời trong Node nhẹ hơn nhiều so với luồng cho mỗi kết nối hoặc quá trình cho mỗi kết nối hệ thống.

Đối với setTimeout(...,0)các cuộc gọi; đôi khi nghỉ giải lao trong một tác vụ tốn thời gian để cho phép các cuộc gọi trong hàng đợi có thể yêu cầu phần xử lý của chúng. Phân chia nhiệm vụ theo nhiều cách khác nhau có thể giúp bạn thoát khỏi những điều này; tuy nhiên, đây không thực sự là một vụ hack, nó chỉ là cách hoạt động của hàng đợi sự kiện. Ngoài ra, sử dụng process.nextTickcho mục đích này tốt hơn nhiều vì khi bạn sử dụng setTimeout, việc tính toán và kiểm tra thời gian đã qua sẽ là cần thiết trong khi process.nextTickđơn giản là những gì chúng tôi thực sự muốn: "Này nhiệm vụ, quay lại cuối hàng đợi, bạn đã sử dụng phần của mình! "


9
Kinh ngạc! Thật tuyệt vời! Tôi thích cách bạn trả lời câu hỏi này! :)
Parth Thakkar

48
Chắc chắn rồi :) Tôi thực sự không thể tin rằng có những người cực kỳ xấu tính ngoài kia lại bỏ phiếu cho bài báo này! Người hỏi gọi nó là "Kinh ngạc chết tiệt!" và một tác giả cuốn sách đề nghị tôi viết trên trang web của anh ấy sau khi nhìn thấy điều này; nhưng một số thiên tài ngoài kia đã bỏ phiếu cho nó. Tại sao bạn không chia sẻ phẩm chất trí tuệ sáng sủa của mình và bình luận về nó thay vì bỏ phiếu kín đáo và lén lút, huh? Tại sao một cái gì đó tốt đẹp lại làm phiền bạn đến vậy? Tại sao bạn lại muốn ngăn điều gì đó hữu ích tiếp cận những người thực sự có thể hưởng lợi từ nó?
hasanyasin

9
Đây không phải là một câu trả lời hoàn toàn công bằng. Điều gì về các tác vụ tính toán tốn kém, nơi chúng ta không thể "nhanh chóng kết thúc" lệnh gọi hàm của mình? Tôi tin rằng một số người sử dụng một số setTimeout(...,0)hack cho việc này, nhưng sử dụng một chuỗi riêng trong trường hợp này chắc chắn sẽ tốt hơn?
mpen

3
@hasanyasin Đây là lời giải thích hay nhất về nút mà tôi tìm thấy cho đến nay! :)
Venemo

7
@Mark Nói chung, nếu nó đắt tiền về mặt tính toán, thì có các tùy chọn / mô-đun cho bộ xử lý / công nhân xử lý ... Nói chung đối với những loại này, tôi sử dụng Hàng đợi Thông báo và có (các) quy trình công nhân xử lý một tác vụ tại một thời gian từ hàng đợi và thực hiện nhiệm vụ đó. Điều này cũng cho phép mở rộng đến nhiều máy chủ. Cùng với những dòng này, Substack có rất nhiều mô-đun hướng đến việc cung cấp và mở rộng quy mô mà bạn có thể xem qua.
Tracker 1

34

(Cập nhật năm 2016: Nhân viên web đang sử dụng io.js - một Node.js fork Node.js v7 - xem bên dưới.)

(Cập nhật 2017: Web worker sẽ không chuyển sang Node.js v7 hoặc v8 - xem bên dưới.)

(Cập nhật 2018: Web workers đang đi vào Node.js Node v10.5.0 - xem dưới đây.)

Một số làm rõ

Sau khi đọc các câu trả lời ở trên, tôi muốn chỉ ra rằng không có gì trong web worker là chống lại triết lý của JavaScript nói chung và Node nói riêng về đồng thời. (Nếu có, nó thậm chí sẽ không được thảo luận bởi WHWG, ít được triển khai hơn nhiều trong các trình duyệt).

Bạn có thể coi web worker như một microservice nhẹ được truy cập không đồng bộ. Không có trạng thái nào được chia sẻ. Không có vấn đề khóa tồn tại. Không có sự ngăn chặn. Không cần đồng bộ hóa. Cũng giống như khi bạn sử dụng một dịch vụ RESTful từ chương trình Node của mình, bạn không lo lắng rằng nó bây giờ là "đa luồng" vì dịch vụ RESTful không nằm trong cùng một chuỗi với vòng lặp sự kiện của riêng bạn. Nó chỉ là một dịch vụ riêng biệt mà bạn truy cập không đồng bộ và đó là điều quan trọng.

Đối với nhân viên web cũng vậy. Nó chỉ là một API để giao tiếp với mã chạy trong một ngữ cảnh hoàn toàn riêng biệt và cho dù nó nằm trong chuỗi khác, quy trình khác, nhóm khác, vùng, vùng chứa hoặc máy khác hoàn toàn không liên quan, vì API không đồng bộ, không chặn, với tất cả dữ liệu được truyền theo giá trị.

Trên thực tế, web worker về mặt khái niệm hoàn toàn phù hợp với Node mà - như nhiều người không biết - tình cờ sử dụng các luồng khá nhiều và trên thực tế "mọi thứ chạy song song ngoại trừ mã của bạn" - xem:

Nhưng các web worker thậm chí không cần phải được triển khai bằng cách sử dụng các luồng. Bạn có thể sử dụng các quy trình, chuỗi màu xanh lá cây hoặc thậm chí là các dịch vụ RESTful trên đám mây - miễn là sử dụng API web worker. Toàn bộ vẻ đẹp của API truyền thông điệp với ngữ nghĩa gọi theo giá trị là việc triển khai bên dưới khá nhiều không liên quan, vì các chi tiết của mô hình đồng thời sẽ không được tiết lộ.

Vòng lặp sự kiện một luồng là hoàn hảo cho các hoạt động liên kết I / O. Nó không hoạt động tốt cho các hoạt động liên quan đến CPU, đặc biệt là những hoạt động chạy lâu. Để làm được điều đó, chúng ta cần tạo ra nhiều quy trình hơn hoặc sử dụng các luồng. Việc quản lý các quy trình con và giao tiếp giữa các quy trình theo cách di động có thể khá khó khăn và nó thường được coi là quá mức cần thiết đối với các tác vụ đơn giản, trong khi sử dụng các luồng có nghĩa là giải quyết các vấn đề về khóa và đồng bộ hóa rất khó thực hiện.

Điều thường được khuyến nghị là chia các hoạt động liên quan đến CPU chạy lâu dài thành các tác vụ nhỏ hơn (giống như ví dụ trong phần "Câu trả lời gốc" trong câu trả lời của tôi cho Tăng tốc độ setInterval ) nhưng nó không phải lúc nào cũng thực tế và nó không sử dụng nhiều hơn nhiều hơn một lõi CPU.

Tôi viết nó để làm rõ những nhận xét về cơ bản nói rằng web worker được tạo ra cho trình duyệt, không phải máy chủ (quên rằng có thể nói về khá nhiều thứ trong JavaScript).

Mô-đun nút

Có một số mô-đun được cho là sẽ thêm Nhân viên web vào Node:

Tôi chưa sử dụng bất kỳ cái nào trong số chúng nhưng tôi có hai nhận xét nhanh có thể liên quan: tính đến tháng 3 năm 2015, node-webworker được cập nhật lần cuối 4 năm trước và node-webworker-thread được cập nhật lần cuối một tháng trước. Ngoài ra, tôi thấy trong ví dụ về việc sử dụng node-webworker-thread mà bạn có thể sử dụng một hàm thay vì tên tệp làm đối số cho hàm tạo Worker, điều này dường như có thể gây ra các vấn đề nhỏ nếu nó được triển khai bằng cách sử dụng các chuỗi chia sẻ bộ nhớ (trừ khi các hàm chỉ được sử dụng cho phương thức .toString () của nó và nếu không được biên dịch trong một môi trường khác, trong trường hợp đó có thể ổn - Tôi phải xem xét sâu hơn về nó, chỉ chia sẻ những quan sát của tôi ở đây).

Nếu có bất kỳ dự án liên quan nào khác triển khai API web worker trong Node, vui lòng để lại nhận xét.

Cập nhật 1

Tôi không biết điều đó nhưng tại thời điểm văn bản, nhưng tình cờ một ngày trước khi tôi viết câu trả lời này Web Workers đã được thêm vào io.js .

( io.js là một nhánh của Node.js - xem: Tại sao io.js quyết định tách Node.js , một cuộc phỏng vấn của InfoWorld với Mikeal Rogers, để biết thêm thông tin.)

Nó không chỉ chứng minh quan điểm rằng không có gì trong web worker đi ngược lại triết lý của JavaScript nói chung và Node nói riêng về tính đồng thời, mà nó có thể dẫn đến việc nhân viên web trở thành công dân hạng nhất trong JavaScript phía máy chủ như io. js (và có thể là Node.js trong tương lai) giống như nó đã có trong JavaScript phía máy khách trong tất cả các trình duyệt hiện đại .

Cập nhật 2

Trong Bản cập nhật 1 và tweet của tôi, tôi đã đề cập đến yêu cầu kéo io.js # 1159 hiện chuyển hướng đến Nút PR # 1159 đã bị đóng vào ngày 8 tháng 7 và được thay thế bằng Nút PR # 2133 - vẫn đang mở. Có một số cuộc thảo luận diễn ra dưới những yêu cầu kéo đó có thể cung cấp thêm một số thông tin cập nhật về trạng thái của nhân viên Web trong io.js / Node.js.

Cập nhật 3

Thông tin mới nhất - cảm ơn NiCk Newman đã đăng nó trong phần bình luận: Có công nhân: cam kết thực hiện ban đầu của Petka Antonov từ ngày 6 tháng 9 năm 2015 có thể được tải xuống và dùng thử trong cây này . Xem bình luận của NiCk Newman để biết chi tiết.

Cập nhật 4

Tính đến tháng 5 năm 2016, các ý kiến ​​cuối cùng về PR # 2133 vẫn còn mở - người lao động: triển khai ban đầu được 3 tháng. Vào ngày 30 tháng 5, Matheus Moreira đã yêu cầu tôi đăng nội dung cập nhật cho câu trả lời này trong phần nhận xét bên dưới và anh ấy đã hỏi tình trạng hiện tại của tính năng này trong phần bình luận PR.

Những câu trả lời đầu tiên trong cuộc thảo luận PR đã gây hoài nghi nhưng sau đó Ben Noordhuis đã viết rằng "Việc hợp nhất cái này thành hình dạng này hay hình dạng khác nằm trong danh sách việc cần làm của tôi cho v7".

Tất cả các ý kiến ​​khác dường như đều tán thành điều đó và kể từ tháng 7 năm 2016, có vẻ như Web worker sẽ có sẵn trong phiên bản tiếp theo của Node , phiên bản 7.0 dự kiến ​​phát hành vào tháng 10 năm 2016 (không nhất thiết phải ở dạng PR chính xác này).

Cảm ơn Matheus Moreira đã chỉ ra điều đó trong các bình luận và phục hồi cuộc thảo luận trên GitHub.

Cập nhật 5

Kể từ tháng 7 năm 2016, có một số mô-đun trên npm không có sẵn trước đây - để có danh sách đầy đủ các mô-đun có liên quan, hãy tìm kiếm npm cho công nhân, nhân viên web, v.v. Nếu bất kỳ điều gì cụ thể phù hợp hoặc không hiệu quả với bạn, vui lòng đăng bình luận.

Cập nhật 6

Kể từ tháng 1 năm 2017 , khó có khả năng web worker sẽ được hợp nhất vào Node.js.

Yêu cầu kéo số 2133 công nhân: việc thực hiện ban đầu bởi Petka Antonov từ ngày 8 tháng 7 năm 2015, cuối cùng đã bị Ben Noordhuis đóng vào ngày 11 tháng 12 năm 2016, người đã nhận xét rằng "hỗ trợ đa luồng thêm quá nhiều chế độ lỗi mới không đủ lợi ích" và "chúng tôi cũng có thể thực hiện điều đó bằng cách sử dụng các phương tiện truyền thống hơn như bộ nhớ dùng chung và tuần tự hóa hiệu quả hơn. "

Để biết thêm thông tin, hãy xem các bình luận cho PR 2133 trên GitHub.

Một lần nữa, cảm ơn Matheus Moreira vì đã chỉ ra điều đó trong các bình luận.

Cập nhật 6

Tôi vui mừng thông báo rằng vài ngày trước, vào tháng 6 năm 2018, web worker đã xuất hiện trong Node v10.5.0 dưới dạng một tính năng thử nghiệm được kích hoạt với --experimental-workercờ.

Để biết thêm thông tin, hãy xem:

🎉🎉🎉 Cuối cùng! Tôi có thể thực hiện bản cập nhật thứ 7 cho câu trả lời Stack Overflow 3 năm tuổi của mình, trong đó tôi lập luận rằng phân luồng nhân viên web không phải là chống lại triết lý của Node, chỉ lần này nói rằng cuối cùng chúng tôi đã hiểu được nó! 😜👍


1
@NiCkNewman Cảm ơn. Tôi thấy rằng yêu cầu kéo ban đầu trong io.js hiện đã bị đóng và được thay thế bằng một yêu cầu khác - với một số thảo luận ở đó trong nhận xét về yêu cầu kéo trên GitHub, có thể bạn sẽ tìm thấy một số thông tin ở đó. Xem: Cập nhật 2 trong câu trả lời của tôi.
rsp

1
Đúng, có vẻ như họ vừa khắc phục sự cố libuv cuối cùng. Tôi tự hỏi khi nào tôi có thể chạm tay vào mô-đun. Không thể đợi được! Cảm ơn vì đã cập nhật cho chúng tôi ~ Chỉnh sửa: Vừa được khởi tạo: github.com/petkaantonov/io.js/commit/… Chúng tôi bắt đầu, nó sắp tới!
NiCk Newman

1
Đúng, nó đang phát trực tiếp. (Chưa được triển khai chính thức) nhưng bạn có thể download source tại đây: github.com/petkaantonov/io.js/tree/… và biên dịch nếu muốn dùng thử! Tôi đang làm điều đó ngay bây giờ ~
NiCk Newman

1
@NiCkNewman Cảm ơn vì thông tin mới - tôi đã thêm nó vào câu trả lời.
rsp

1
Bạn có thể vui lòng cập nhật cho chúng tôi về trạng thái workerstriển khai Node.js không? Nhận xét mới nhất trong PR # 2133 là từ tháng Hai; các nhà phát triển dường như đã gặp sự cố và không có nhận xét nào cho thấy nó đã được giải quyết.
Matheus Moreira

8

Tôi đến từ trường phái suy nghĩ cũ, nơi chúng tôi sử dụng đa luồng để làm cho phần mềm nhanh chóng. Trong 3 năm qua, tôi đã sử dụng Node.js và rất ủng hộ nó. Như hasanyasin đã giải thích chi tiết cách hoạt động của nút và khái niệm về chức năng không đồng bộ. Nhưng hãy để tôi thêm vài điều ở đây.

Ngày xưa với lõi đơn và tốc độ xung nhịp thấp hơn, chúng tôi đã thử nhiều cách khác nhau để phần mềm hoạt động nhanh và song song. trong những ngày DOS, chúng tôi sử dụng để chạy một chương trình tại một thời điểm. Ngoài windows, chúng tôi bắt đầu chạy nhiều ứng dụng (quy trình) cùng nhau. Các khái niệm như phủ đầu và không phủ đầu (hoặc hợp tác) khi được thử nghiệm. bây giờ chúng ta biết rằng preemptive là câu trả lời cho tác vụ đa xử lý tốt hơn trên máy tính lõi đơn. Cùng với đó là các khái niệm về quy trình / nhiệm vụ và chuyển đổi ngữ cảnh. Hơn khái niệm về luồng để giảm bớt gánh nặng của quá trình chuyển đổi ngữ cảnh. Chủ đề được tạo ra như một giải pháp thay thế trọng lượng nhẹ để tạo ra các quy trình mới.

Vì vậy, dù muốn hay không tín hiệu luồng hay không đa lõi hay lõi đơn thì các quy trình của bạn sẽ được ưu tiên và thời gian cắt giảm bởi hệ điều hành.

Nodejs là một quy trình duy nhất và cung cấp cơ chế không đồng bộ. Ở đây các công việc được gửi đến dưới hệ điều hành nằm để thực hiện các tác vụ trong khi chúng ta chờ đợi trong một vòng lặp sự kiện để tác vụ kết thúc. Khi nhận được tín hiệu xanh từ OS, chúng tôi sẽ thực hiện những việc cần làm. Theo một cách nào đó, đây là đa tác vụ hợp tác / không phủ đầu, vì vậy chúng ta không bao giờ nên chặn vòng lặp sự kiện trong một khoảng thời gian rất dài, nếu không, chúng ta sẽ làm suy giảm ứng dụng của mình rất nhanh.
Vì vậy, nếu có một tác vụ nào đó bị chặn trong tự nhiên hoặc rất tốn thời gian, chúng tôi sẽ phải phân nhánh nó ra thế giới ưu tiên của hệ điều hành và các luồng. có những ví dụ điển hình về điều này trong tài liệu libuv . Ngoài ra nếu bạn đọc tài liệu thêm bạn thấy rằng FileI / O được xử lý trong chủ đề trong Node.js .

Vì vậy, trước hết tất cả trong thiết kế phần mềm của chúng tôi. Thứ hai, việc chuyển đổi ngữ cảnh luôn diễn ra bất kể họ nói gì với bạn. Các luồng ở đó và vẫn ở đó là có lý do, lý do là chúng chuyển đổi giữa các quy trình nhanh hơn.

Dưới mui xe trong node.js tất cả c ++ và luồng của nó. Và nút cung cấp cách c ++ để mở rộng chức năng của nó và tăng tốc hơn nữa bằng cách sử dụng các luồng mà chúng bắt buộc phải có, tức là chặn các tác vụ như đọc từ nguồn đang ghi sang nguồn, phân tích dữ liệu lớn, v.v.

Tôi biết câu trả lời hasanyasin là câu trả lời được chấp nhận nhưng đối với tôi, các chủ đề sẽ tồn tại bất kể bạn nói gì hoặc làm thế nào bạn ẩn chúng đằng sau các tập lệnh, thứ hai là không ai chỉ phá mọi thứ vào các luồng chỉ vì tốc độ mà chủ yếu được thực hiện cho các tác vụ chặn. Và các luồng nằm trong phần xương sau của Node.js nên trước khi hoàn toàn xử lý đa luồng là chính xác. Ngoài ra, các luồng khác với các quy trình và giới hạn của việc có các quy trình nút trên mỗi lõi không áp dụng chính xác cho số luồng, luồng giống như các nhiệm vụ con đối với một quy trình. trên thực tế, các chủ đề đã thắng; không hiển thị trong trình quản lý tác vụ windows của bạn hoặc lệnh hàng đầu linux. một lần nữa chúng có trọng lượng nhỏ hơn sau đó xử lý


Mã không đồng bộ không phải là một số đổi mới lớn (thực tế là chúng ta đã có nó trong nhiều thập kỷ) và đa luồng không phải là một số công nghệ không còn được thay thế nữa. Chúng là những công cụ khác nhau với sự cân bằng khác nhau và trên thực tế, chúng thậm chí có thể được kết hợp với nhau khá tốt. Mỗi khi bạn chạy node-cluster, trên thực tế, bạn chạy nhiều "luồng" (trong trường hợp này là các quy trình, nhưng điều tương tự cũng có thể đạt được với các luồng và thậm chí còn nhẹ hơn). Hoặc lấy Erlang hoặc Go, có thể chạy hàng ngàn chủ đề màu xanh lá cây ...
Hejazzman

Tôi nghĩ rằng điểm chính mà chúng tôi còn thiếu là quy trình trong Hệ điều hành sẽ luôn được thực hiện theo cách thức phủ đầu để mang lại sự công bằng. Ngoài ra với nhiều bộ xử lý, bạn có thể thực hiện mã song song thực tế nhưng ngay cả khi đó bạn sẽ có quyền ưu tiên. Công việc không đồng bộ cũng được thực hiện bởi HĐH trong một số quá trình.
limplash

4

Tôi không chắc liệu nhân viên web có liên quan trong trường hợp này hay không, họ là công nghệ phía máy khách (chạy trong trình duyệt), trong khi node.js chạy trên máy chủ. Các sợi, theo như tôi hiểu, cũng đang chặn, tức là chúng là đa nhiệm tự nguyện, vì vậy bạn có thể sử dụng chúng, nhưng nên tự quản lý các công tắc ngữ cảnh yield. Các chủ đề thực sự có thể là thứ bạn cần, nhưng tôi không biết chúng đã trưởng thành như thế nào trong node.js.


3
chỉ để biết thông tin của bạn, những người làm web đã được điều chỉnh (một phần) trên node.js. Và có sẵn dưới dạng node-workersgói. Hãy xem cái này: github.com/cramforce/node-worker
Parth Thakkar

Tốt để biết, cảm ơn. Tuy nhiên, tài liệu rất khan hiếm, tôi không biết liệu nó có chạy trong một chuỗi, quy trình riêng biệt hay chỉ đơn giản là chạy trong cùng một quy trình và tôi không có thời gian để tìm hiểu mã, vì vậy tôi không biết liệu nó có làm việc cho trường hợp của bạn.
lanzz

@ParthThakkar: Dự án đó đã không được động đến trong 3 năm (2 khi bạn đăng) và chưa vượt qua 0.0.1.
mpen

@Mark: Lý do cho sự thiếu hiểu biết của tôi về điều đó là tôi chưa phải là một lập trình viên chuyên nghiệp. Heck, tôi thậm chí không ở trong một trường đại học. Tôi vẫn là một học sinh Trung học, người vẫn tiếp tục đọc về lập trình - bên cạnh việc quản lý công việc ở trường. Vì vậy, tôi không thể có kiến ​​thức về tất cả các vấn đề như vậy từ xa. Tôi chỉ đăng tải những gì tôi biết ...
Parth Thakkar

@Mark: Mặc dù rất vui khi bạn chỉ ra điều đó về lịch sử của dự án. Những điều như vậy sẽ được lưu tâm trong các câu trả lời trong tương lai của tôi !! :)
Parth Thakkar

3

worker_threadsđã được thực hiện và chuyển sau một lá cờ trong node@10.5.0. Đây vẫn là bản triển khai ban đầu và cần nhiều nỗ lực hơn để làm cho nó hiệu quả hơn trong các bản phát hành sau này. Đáng để thử trong thời gian gần nhất node.


2

Theo nhiều ý kiến ​​của các nhà phát triển Node, một trong những phần hay nhất của Node là bản chất đơn luồng của nó. Các chủ đề giới thiệu một loạt các khó khăn với tài nguyên được chia sẻ mà Node hoàn toàn tránh được bằng cách không làm gì khác ngoài IO không chặn.

Điều đó không có nghĩa là Node bị giới hạn trong một luồng duy nhất. Chỉ là phương pháp để nhận được sự đồng thời theo luồng khác với những gì bạn đang tìm kiếm. Cách tiêu chuẩn để xử lý các luồng là sử dụng mô-đun cụm có tiêu chuẩn với chính Node. Đó là một cách tiếp cận các luồng đơn giản hơn là xử lý thủ công chúng trong mã của bạn.

Để xử lý lập trình không đồng bộ trong mã của bạn (như trong, tránh các kim tự tháp gọi lại lồng nhau), thành phần [Tương lai] trong thư viện Fibers là một lựa chọn phù hợp. Tôi cũng khuyên bạn nên xem Asyncblock dựa trên Fibers. Các sợi tốt vì chúng cho phép bạn ẩn lệnh gọi lại bằng cách sao chép ngăn xếp và sau đó nhảy giữa các ngăn xếp trên một luồng đơn khi chúng cần. Tiết kiệm cho bạn những rắc rối của các chủ đề thực trong khi mang lại cho bạn những lợi ích. Nhược điểm là các dấu vết ngăn xếp có thể hơi kỳ lạ khi sử dụng Fibers, nhưng chúng không quá tệ.

Nếu bạn không cần phải lo lắng về nội dung không đồng bộ và chỉ muốn thực hiện nhiều xử lý mà không bị chặn, một cuộc gọi đơn giản đến process.nextTick (gọi lại) thỉnh thoảng là tất cả những gì bạn cần.


tốt, gợi ý của bạn - về các cụm - là những gì tôi nghĩ đến ban đầu. Nhưng vấn đề với điều đó là chi phí của chúng - một phiên bản mới của v8 phải được khởi tạo mỗi khi tiến trình mới được phân nhánh (~ 30ms, 10MB). Vì vậy, bạn không thể tạo nhiều trong số chúng. Điều này được lấy trực tiếp từ tài liệu nút: Các nút con này (về quy trình con) vẫn là các phiên bản hoàn toàn mới của V8. Giả sử khởi động ít nhất 30ms và bộ nhớ 10mb cho mỗi Node mới. Có nghĩa là, bạn không thể tạo ra nhiều nghìn người trong số họ.
Parth Thakkar

1
Đây chính xác là ý tưởng của cụm. Bạn chạy một nhân viên cho mỗi lõi cpu. Nhiều hơn nữa là rất có thể là không cần thiết. Ngay cả các tác vụ chuyên sâu về cpu cũng sẽ hoạt động tốt với kiểu không đồng bộ. Tuy nhiên, nếu bạn thực sự cần các luồng toàn diện, có lẽ bạn nên xem xét chuyển hoàn toàn sang phần phụ trợ máy chủ khác.
genericdave

1

Có thể một số thông tin khác về những công việc bạn đang thực hiện sẽ hữu ích. Tại sao bạn cần (như bạn đã đề cập trong nhận xét của mình cho câu trả lời của genericdave) lại cần tạo nhiều nghìn trong số chúng? Cách thông thường để thực hiện loại việc này trong Node là khởi động một quy trình công nhân (sử dụng fork hoặc một số phương pháp khác), quy trình này luôn chạy và có thể được giao tiếp bằng cách sử dụng các thông báo. Nói cách khác, không khởi động nhân viên mới mỗi khi bạn cần thực hiện bất kỳ tác vụ nào mà nó đang làm, mà chỉ cần gửi tin nhắn đến nhân viên đã chạy và nhận phản hồi khi hoàn thành. Thành thật mà nói, tôi không thể thấy rằng việc khởi động hàng nghìn luồng thực tế cũng sẽ rất hiệu quả, bạn vẫn bị giới hạn bởi CPU của bạn.

Bây giờ, sau khi nói tất cả những điều đó, tôi đã làm rất nhiều việc với Hook.io gần đây, nó dường như hoạt động rất tốt cho loại tác vụ giảm tải này sang các quy trình khác, có thể nó có thể hoàn thành những gì bạn cần.

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.