Đừng chặn vòng lặp sự kiện (Don't block the Event Loop) - Phần 1

Đừng chặn vòng lặp sự kiện (Don't block the Event Loop) - Phần 1

Vấn đề

Nếu bạn đang viết bất cứ ứng dụng nào phức tạp, thì việc đọc bài viết này sẽ giúp bạn viết các ứng dụng hiệu suất cao hơn, an toàn hơn.

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu node.js xử lý công việc như thế nào? Cũng như những loại mã nào được Event Loop hay Worker Pool xử lý?

Tóm tắt

Node.js xử lý mã JavaScript trong Event Loop và cung cấp Worker Pool để xử lý các tác vụ tốn kém như I/O. Node.js có khả năng mở rộng tốt, đôi khi còn tốt hơn các phương pháp tiếp cận nặng đô như là máy chủ Apache. Khả năng mở rộng của Node.js nằm ở chỗ nó sử dụng một số lượng nhỏ các luồng để xử lý nhiều client. Nếu Node.js có thể thực hiện với ít luồng hơn, thì nó có thể tiết kiệm được nhiều thời gian và bộ nhớ của hệ thống hơn thay vì tiêu tốn cho các luồng (bộ nhớ, context). Cũng vì Node.js chỉ có một vài luồng, chúng ta phải cấu trúc ứng dụng của mình để sử dụng chúng một cách tối ưu nhất.

Nên nhớ một điều: Node.js nhanh khi công việc liên quan đến mỗi client tại bất kỳ thời điểm nào là "nhỏ". Tức là các đoạn mã tại một thời điểm bất kì được xử lý càng nhanh càng tốt.

Điều này áp dụng cho cả các hàm callbacks và các tiến trình đang được xử lý trên Worker Pool.

Tại sao tôi nên tránh block (chặn) Event Loop và Worker Pool?

Node.js sử dụng một lượng nhỏ các luồng để xử lý nhiều kết nối. Trong Node.js có hai loại luồng: một Event Loop (hay còn gọi là vòng lặp chính, luồng chính, luồng sự kiện, v.v.) và một nhóm gồm n luồng trong Worker Pool.

Nếu một luồng mất nhiều thời gian để có thể thực hiện callback nó được gọi là "bị chặn". Trong khi một luồng bị chặn nó sẽ không thể xử lý các yêu cầu từ client nào khác. Điều này sẽ gây ra 2 vấn đề:

  • Hiệu suất: Nếu thường xuyên thực hiện hoạt động nặng trên một trong hai loại luồng thì thông lượng (request/s) máy chủ sẽ bị ảnh hưởng.

  • Bảo mật: Nếu đối với một số đầu vào nhất định gây ảnh hưởng về hiệu suất xử lý. Máy chủ sẽ hoàn toàn bị tê liệt. Đó là các cuộc tấn công DDos.

Đánh giá nhanh về Node.js

Node.js sử dụng Kiến trúc Event-Driven: nó có Event Loop để điều phối và Worker Pool dành cho các tác vụ I/O tốn kém.

Mã nào chạy trên Event Loop?

Khi khởi động các ứng dụng Node.js, đầu tiên nó sẽ hoàn thành giai đoạn khởi tạo, nhập các module và "đăng ký" các hàm callbacks cho các sự kiện. Các ứng dụng Node.js sau đó đi vào Event Loop, phản hồi các yêu cầu của client bằng cách thực hiện hàm callback thích hợp. Hàm callback này được thực thi đồng bộ và có thể "đăng ký" các yêu cầu không đồng bộ để tiếp tục xử lý sau khi hoàn tất. Các hàm callbacks cho các yêu cầu không đồng bộ này cũng sẽ được thực hiện trên Event Loop.

Event Loop cũng sẽ đáp ứng các yêu cầu không đồng bộ không chặn được thực hiện bởi các hàm callback của nó, ví dụ: network I/O.

Tóm lại, Event Loop thực thi các hàm callbacks JavaScript đã "đăng ký" cho các sự kiện và cũng chịu trách nhiệm thực hiện các yêu cầu không đồng bộ không chặn như network I/O.

Mã nào chạy trên Worker Pool?

Worker Pool của Node.js được triển khai trong libuv.

Node.js sử dụng Worker Pool để xử lý các tác vụ "tốn kém". Điều này bao gồm các tác vụ I/O mà hệ điều hành không cung cấp phiên bản non-blocking, cũng như các tác vụ đặc biệt đòi hỏi nhiều CPU.

Đây là các API modules sử dụng Worker Pool

  • I/O:

  • DNS : dns.lookup(), dns.lookupService().

  • File system: Tất cả các API hệ thống tệp ngoại trừ fs.FSWatcher() và những API đồng bộ sử dụng threadpool của libuv.

  • CPU:

  • Crypto: crypto.pbkdf2(), crypto.scrypt(), crypto.randomBytes(), crypto.randomFill(), crypto.generateKeyPair().

  • Zlib: Tất cả các API zlib ngoại trừ những API đồng bộ sử dụng threadpool của libuv.

Trong nhiều ứng dụng Node.js, các API này là nguồn tác vụ duy nhất cho Worker Pool. Các ứng dụng và modules sử dụng C++ add-on có thể gửi các tác vụ khác đến Worker Pool.

Làm cách nào để Node.js quyết định mã nào sẽ chạy tiếp theo?

Tóm lại là Event Loop và Worker Pool duy trì hàng đợi cho các sự kiện đang chờ xử lý và nhiệm vụ đang chờ xử lý tương ứng.

Trên thực tế, Event Loop không thực sự duy trì một hàng đợi. Thay vào đó, nó có một tập hợp các bộ mô tả tệp mà nó yêu cầu hệ điều hành giám sát, sử dụng cơ chế như epoll (Linux), kqueue (OSX), event ports (Solaris) hoặc IOCP (Windows). Các bộ mô tả tệp này tương ứng với các network sockets, bất kỳ tệp nào mà nó đang theo dõi, v.v. Khi hệ điều hành thông báo rằng một trong những bộ mô tả tệp này đã sẵn sàng, Event Loop sẽ dịch nó thành sự kiện thích hợp và gọi (các) lệnh gọi lại (callbacks) được liên kết với sự kiện đó.

Ngược lại, Worker Pool thì lại sử dụng một hàng đợi thực thụ có chứa các tác vụ cần được xử lý. Một Worker lấy ra một nhiệm vụ từ hàng đợi này và xử lý nó, đến khi hoàn thành Worker phát ra sự kiện "Ít nhất một nhiệm vụ đã hoàn thành" cho Event Loop biết.

Điều này có ý nghĩa gì đối với thiết kế ứng dụng?

Trong hệ thống tạo mỗi luồng cho mỗi client như Apache, mỗi client đang chờ xử lý được chỉ định một luồng riêng. Nếu một luồng xử lý một client mất nhiều thời gian, hệ điều hành sẽ ngắt nó và cho một client khác thực hiện. Do đó, hệ điều hành đảm bảo rằng các client yêu cầu một lượng công việc nhỏ sẽ không bị chờ bởi các máy khách yêu cầu nhiều công việc hơn.

Vì Node.js xử lý nhiều client với ít luồng, nếu một luồng chặn xử lý yêu cầu của một client, thì các yêu cầu khác đang chờ xử lý có thể không được xử lý cho đến khi nó hoàn thành. Do đó, đối xử công bằng với client là trách nhiệm của ứng dụng của chúng ta. Điều này có nghĩa là bạn không nên mất quá nhiều thời gian cho một yêu cầu nào từ client.

Đây là một phần lý do tại sao Node.js có thể tốt trong khả năng mở rộng, nhưng nó cũng có nghĩa là chúng ta phải có trách nhiệm đảm bảo việc công bằng.

Tổng kết

Bài viết này chúng ta đã thảo luận về cách xử lý các tác vụ I/O của Event Loop và Worker Poll. Nó giải thích tại sao chúng ta lại không nên chặn Event Loop, sự thật thì Event Loop xử lý những gì còn Worker Poll xử lý những gì! Bài viết sau chúng ta sẽ cùng tìm hiểu xem chuyện gì xảy ra nếu chúng ta "chặn" Event Loop và những đoạn mã mà sẽ gây ra hiện tượng "chặn" Event Loop.

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

Bình luận (0)