Nhóm công nhân NodeJS

Các từ khóa awaitasync được các nhà phát triển JavaScript yêu thích vì cách chúng cho phép viết logic không đồng bộ phức tạp một cách tao nhã. Nhưng có một điều không thể với họ. đồng thời 'thực' trong đó nhiều đoạn mã chạy cùng một lúc. Kiến trúc của JavaScript được thiết lập theo cách luôn có một quy trình duy nhất chạy trên một luồng duy nhất thực thi tất cả mã. Hành vi không đồng bộ đạt được bằng cách sử dụng hàng đợi chứa các đoạn mã tiếp theo sẽ được thực thi. Với lập lịch chiến thuật trên hàng đợi này, một chức năng có thể bị tạm dừng cho đến khi hoàn thành một số tác vụ nền nhất định chạy bên ngoài quy trình nút, chẳng hạn như thao tác tệp hoặc cuộc gọi mạng. Nhưng không thể để các chức năng JavaScript khác tiếp tục trong khi một chức năng JavaScript khác đang chạy

Một ví dụ về cách chặn tiến trình chính trong nút

Trong mẫu mã ở trên, lệnh gọi api sẽ được thực hiện, nhưng mọi thứ trong hàm api sau đó sẽ không bao giờ được thực thi vì quá trình này bị chặn bởi vòng lặp while. Điều này có thể trở thành một vấn đề nghiêm trọng trong các ứng dụng nút lớn xử lý nhiều dữ liệu. Để giải quyết vấn đề này người lao động nơi giới thiệu. Chúng cho phép các nhà phát triển bắt đầu một quy trình nút hoàn toàn mới chạy tập lệnh và có thể giao tiếp với quy trình chính thông qua hệ thống sự kiện. Tuy nhiên, đây thực sự là một phiên bản nút mới và không có bộ nhớ dùng chung như các hệ thống dựa trên luồng có (e. g. đa luồng trong C# hoặc Java). Điều này có nghĩa là khi bạn gửi dữ liệu cho một worker, nó sẽ được sao chép và tạo lại trong thời gian chạy khác. Điều này có thể gây nhầm lẫn khi chuyển các đối tượng xung quanh và thay đổi chúng trong một quy trình. Những đột biến đó sẽ không được phản ánh ở phía bên kia. Các mô-đun cũng sẽ có một phiên bản cho mỗi quy trình nút, phá vỡ hành vi đơn lẻ mặc định của chúng

Nguồn tổng hợp

Trong đoạn trước, chúng ta đã thảo luận rằng việc tạo worker sẽ tạo một phiên bản nút mới. Điều này có nghĩa là tạo ra những người lao động mới là một nhiệm vụ tương đối nặng nề. Cũng có nguy cơ tạo ra nhiều công nhân và làm ngập hệ thống máy chủ với các quy trình. Đây là nơi tổng hợp tài nguyên xuất hiện. Chúng tôi sẽ tạo một lượng công nhân nhất định khi chúng tôi khởi động ứng dụng của mình và sử dụng lại số lượng đó mỗi khi chúng tôi muốn chạy một tác vụ trên một quy trình khác. Nhóm công nhân này sẽ được quản lý bởi người quản lý nhóm, người nhận nhiệm vụ và sẽ giao chúng cho một công nhân nhàn rỗi. Bản thân các chức năng sẽ được bao bọc trong giao diện Task có các phương thức để dễ dàng xâu chuỗi và chạy các tác vụ. Chúng ta có thể sử dụng Lời hứa để dễ dàng xử lý mã không đồng bộ 'thực' này, giống như với các hoạt động của mạng và tệp mà máy chủ nút chạy cho chúng ta trong nền

Tạo một hồ bơi

Chúng tôi sẽ bắt đầu bằng cách xác định hình dạng chung của kiến ​​trúc của chúng tôi. Sẽ có một nhóm công nhân chứa tất cả công nhân và hàng đợi với các tác vụ. Tôi quyết định cung cấp cho workerpool một phương thức công khai. createTask. Phương pháp này sẽ tạo một tác vụ sẽ chạy bên trong nhóm mà nó đã được tạo trong. Giao diện Task cũng có một phương thức công khai duy nhất. runAsync. Phương pháp này sẽ chạy chức năng được bao bọc trên một công nhân. Giá trị trả về của hàm này là một Lời hứa sẽ giải quyết khi một worker thực hiện xong hàm. Các giao diện đó được định nghĩa như sau

Nhiệm vụ là một hộp xung quanh một chức năng làm cho nó không đồng bộ

Việc triển khai các giao diện đó sẽ bắt đầu bằng việc tạo một hàm tạo cho nhóm công nhân. Chức năng này bắt đầu bằng việc xác định tất cả các phần bên trong của nhóm

  • bản đồ id và công nhân chứa tất cả công nhân là một phần của nhóm của chúng tôi. Những công nhân đó sẽ được tạo một lần duy nhất và được sử dụng lại cho tất cả các tác vụ mà nhóm công nhân sẽ thực hiện
  • một bộ sưu tập có id của các công nhân đang rảnh và sẵn sàng nhận nhiệm vụ mới
  • tồn đọng với các nhiệm vụ đã được xếp hàng đợi và sẽ được thực hiện càng sớm càng tốt. Những tác vụ đó tồn tại của một id, một chức năng và dữ liệu bổ sung để gửi cho nhân viên. Tất cả dữ liệu ở đây được gõ là any, kiểu an toàn sẽ được giới thiệu trong giao diện Task
  • một bản đồ với taskid và trình giải quyết. Trình giải quyết là chức năng sẽ được gọi khi một công nhân đã hoàn thành nhiệm vụ. Mỗi nhiệm vụ sẽ có trình giải quyết riêng
  • một bộ đếm sẽ được sử dụng để gán số nhận dạng nhiệm vụ gia tăng

Chúng tôi cho phép thế giới bên ngoài xác định kích thước của hồ bơi

Các công nhân được xây dựng với một tập lệnh sẽ là quy trình chính của phiên bản nút đó. Chúng tôi sẽ xác định nội dung của nó sau này. Với những định nghĩa đó, chúng ta có thể bắt đầu thêm logic để thực thi các tác vụ. Chúng tôi sẽ bắt đầu điều này bằng cách tạo một chức năng sẽ chạy các tác vụ tiếp theo trên hồ sơ tồn đọng nếu có một công nhân sẵn sàng. Đối với điều này, một số bước nhất định phải được thực hiện

  1. kiểm tra xem chúng ta có cả nhiệm vụ và nhân viên nhàn rỗi không
  2. nhận nhiệm vụ tiếp theo và công nhân nhàn rỗi
  3. xây dựng thông điệp cho người lao động. Chúng tôi không thể gửi các chức năng giữa các công nhân nên chúng tôi phải biến nó thành một chuỗi
  4. gửi nhiệm vụ cho worker

Cuối cùng, chúng tôi sẽ gọi lại runNext. Bằng cách này, chúng tôi sẽ tiếp tục lập lịch tác vụ cho công nhân miễn là chúng tôi còn công nhân nhàn rỗi và nhiệm vụ. runNext nên được gọi mỗi khi chúng tôi chạm vào công việc tồn đọng hoặc bộ sưu tập async1. Bằng cách này, chúng tôi sẽ luôn sắp xếp các nhiệm vụ càng sớm càng tốt

Gọi toString trên một chức năng cung cấp cho mã nguồn

Sau đây là lúc tạo tập lệnh sẽ chạy bên trong công nhân. Tập lệnh này sẽ lắng nghe các nhiệm vụ đến. Khi một tác vụ đến, nó sẽ chạy chúng và gửi lại kết quả cùng với mã định danh. Để chạy chức năng của chúng tôi, trước tiên chúng tôi sẽ phải biến nó thành chức năng được đặt tên. Sau đó, chúng ta có thể sử dụng async2 để chạy biên dịch và chạy nó. Kịch bản có thể được tìm thấy dưới đây

Chúng tôi sử dụng một số phép thuật js để đảm bảo rằng chúng tôi luôn đánh giá một chức năng được đặt tên

Điều tiếp theo sẽ xảy ra là workerpool nhận kết quả và giải quyết tác vụ. Đối với điều này, chúng tôi thêm một trình nghe async3 cho tất cả các công nhân. Ở đây, chúng tôi gọi hàm trình phân giải cho id tác vụ và đưa nhân viên vào danh sách các tác vụ nhàn rỗi. Cuối cùng, chúng tôi gọi runNext để công việc tồn đọng sẽ được xử lý và nhân viên nhàn rỗi mới nhận nhiệm vụ

Lưu ý rằng chúng tôi cũng chuyển kết quả giữa các công nhân, vì vậy họ cũng phải có thể sao chép Tạo các tác vụ

Bây giờ chúng ta đã có phần bên trong của workerpool, chúng ta có thể bắt đầu tạo và xếp hàng các tác vụ. Các tác vụ sẽ được tạo bởi một hàm được trả về từ async5. Điều này có nghĩa là một tác vụ luôn được giới hạn trong một nhóm công nhân cụ thể. Kết quả là cấu trúc của chúng ta sẽ trông như thế này

Trả về các đối tượng từ các chức năng có nghĩa là chúng tôi nắm bắt nhóm bên trong các tác vụ

Bên trong createTask không có gì xảy ra, nó chỉ trả về một đối tượng có chức năng sẽ chạy tác vụ và sử dụng nhóm. Để chạy tác vụ, chúng ta phải thực hiện các bước sau

  1. tạo một nhiệm vụ mới
  2. thêm nhiệm vụ vào hồ sơ tồn đọng
  3. tạo một Lời hứa và thêm trình phân giải vào trình phân giải
  4. trả lại lời hứa
  5. gọi runNext để khởi động hệ thống

Lưu ý rằng chúng tôi đặt trình phân giải bên trong hàm tạo Promise thành bản đồ ở mức đóng cao hơn. Bằng cách này, chúng ta có thể trả lại Promise và giải quyết nó sau này từ bên ngoài

Quá trình chính chỉ chạy một chức năng tại một thời điểm, vì vậy chúng tôi không nhận được các điều kiện cạnh tranh trong taskIdCounter

Bây giờ là lúc để cho hệ thống của chúng ta chạy thử lần đầu tiên. Trong ví dụ bên dưới, tôi chạy 3 hàm trong nền tính toán dãy Fibonacci gồm các số khác nhau. Tôi bạn đọc đầu ra của bàn điều khiển, bạn sẽ thấy rằng tác vụ chạy song song và kết thúc vào các thời điểm khác nhau

Chúng tôi không thể tạo đệ quy async8 vì nó sẽ được chạy trong một phiên bản nút khác nơi mà async8 không tồn tại Chuỗi nhiệm vụ

Một tính năng thường được sử dụng của Lời hứa là xâu chuỗi nhiều trong số chúng theo trình tự trong đó mỗi lời hứa tiếp theo phụ thuộc vào đầu ra của lời hứa trước đó. Cuộc gọi api trong phần giới thiệu của bài viết này là ví dụ tốt về điều này. Chúng tôi sử dụng kết quả của api0 để gọi api1 và kết quả của chúng tôi đã sử dụng để trích xuất thuộc tính api2. Tôi cũng sẽ thêm chức năng api3 vào giao diện Task của chúng tôi để cho phép loại chuỗi quy trình nền này. Giao diện cập nhật trông như thế này

Chúng tôi xâu chuỗi với một nhiệm vụ đi từ kết quả của chúng tôi đến kết quả mới

Để triển khai, chúng tôi đơn giản ghi đè hàm runAsync để chạy tác vụ hiện tại trước tiên và khi điều đó đã được giải quyết, hãy chạy tác vụ tiếp theo. Với chức năng này, chúng tôi có thể kết hợp vô tận các tác vụ với nhau để tạo ra các quy trình lớn hơn giúp sử dụng tối ưu khả năng xếp hàng của workerpool

Điều này trên đối tượng chữ hoạt động khác nhau dựa trên việc bạn có sử dụng api6 hay không

Tính năng này được sử dụng như thế này

Chuỗi nhiệm vụ hoạt động giống như với lời hứaKết luận

Trong bài viết này, tôi đã thảo luận về một số điểm yếu lớn nhất và các tính năng mạnh nhất của JavaScript. Xung quanh vấn đề này, chúng tôi đã viết một nhóm công nhân được quản lý cho phép chúng tôi đi xa hơn theo con đường đồng thời mà JavaScript đã từng được thiết kế để thực hiện. Mã nguồn có thể được tìm thấy trên Github. Bài viết này là phần tiếp theo của bài viết mới nhất của tôi trên ITNEXT mà bạn có thể thích

Nhóm công nhân Nodejs là gì?

Nhóm công nhân của nút. js được triển khai trong libuv (tài liệu), hiển thị API gửi tác vụ chung . Nút. js sử dụng Nhóm công nhân để xử lý các tác vụ "đắt tiền". Điều này bao gồm I/O mà hệ điều hành không cung cấp phiên bản không chặn, cũng như các tác vụ đặc biệt sử dụng nhiều CPU.

Làm cách nào để tạo nhóm công nhân trong nodejs?

nhóm nhân viên .
Đặc trưng. dễ sử dụng. .
Tại sao. JavaScript dựa trên một vòng lặp sự kiện duy nhất xử lý một sự kiện tại một thời điểm. .
Cài đặt. Cài đặt qua npm. npm cài đặt workerpool
Trọng tải. Để tải workerpool trong một nút. js (cả ứng dụng chính cũng như worker). .
Sử dụng. Chức năng giảm tải động. .
ví dụ. .
API. .
lộ trình

Nhóm công nhân là gì?

Mẫu nhóm công nhân là thiết kế trong đó một số lượng công nhân cố định được giao một luồng nhiệm vụ để xử lý trong hàng đợi . Các nhiệm vụ ở trong hàng đợi cho đến khi một nhân viên rảnh rỗi nhận nhiệm vụ và thực hiện nó. Nhóm công nhân rất tuyệt vời để kiểm soát việc thực thi đồng thời cho một tập hợp các công việc đã xác định.

Có bao nhiêu luồng công nhân trong nodejs?

Nút. js là thời gian chạy đơn luồng . Điều này xuất phát từ JavaScript là ngôn ngữ chặn đồng bộ, chạy mọi thứ trong một luồng. Nút.