Hôm nay, trong bài viết này, mình sẽ thảo luận với các bạn về hoạt động của Node.js. Những gì xảy ra khi code chạy trong môi trường thực thi của Node. Các thư viện chính, các Dependency và bộ não của node.js.

Node.js không chỉ được viết bằng Javascript mà được viết bằng c ++ và cả Javascript để có thể hoạt động chính xác. Có rất nhiều thư viện mà Node phụ thuộc vào. Tuy nhiên, V8 và LIBUV là hai dependency quan trọng nhất xử lý phần lớn các hoạt động của node.js.

Hai dependency này cung cấp một lớp trừu tượng cực nuột và cho phép chúng ta viết mã JavaScript thuần chạy trong Node.js mà vẫn cho phép truy cập vào các tính năng đọc file được triển khai trong LIBUV và các thư viện C ++ khác. Sự hay ho ở đây là là Node.js liên kết tất cả các thư viện này với nhau, bất kể chúng được viết bằng C ++ hay JavaScript, và sau đó cho phép chúng ta truy cập vào các chức năng của chúng bằng JavaScript thuần. Vì vậy, chúng ta tập trung vào code ứng dụng dễ dàng hơn nhiều mà không cần phải xử lý mã C ++.

Cùng tìm hiểu kĩ từng dependency một nhé

Tham khảo khoá học Lộ trình NodeJS

V8

Node.js được xây dựng trên V8 engine của Google. Nó là công cụ javascript nhanh nhất. V8 engine chuyển đổi code javascript thành mã máy mà máy tính thực sự hiểu được. Kết quả sau đó được tạo và trả về node.js. Node.js không thể hiểu mã javascript mà chúng ta viết mà không có V8. Bên cạnh javascript, V8 cũng sử dụng C ++.

Trước khi Bắt đầu với LIBUV, bạn nên hiểu cơ bản về I/O không đồng bộ

Đầu vào-đầu ra không đồng bộ

I/O không đồng bộ cho phép các ứng dụng xử lý chồng chéo với các hoạt động I/O. Nói cách đơn giản, mục tiêu là chương trình không bao giờ bị nghẽn lại

Trong I/O đồng bộ, thread - Luồng (một luồng chỉ là một chuỗi các lệnh) sẽ đợi cho đến khi toàn bộ hoạt động kết thúc. Mặt khác trong I/O không đồng bộ, luồng không đợi trong các hoạt động. Thao tác I/O sẽ chạy ở chế độ nền và nó sẽ được gọi khi kết thúc. Tính năng I/O không đồng bộ cho phép ứng dụng có thêm thời gian để thực hiện các xử lý khác trong khi I/O đang diễn ra.

LIBUV

LIBUV là một dependency node.js. Nó cung cấp cho node.js quyền truy cập vào hệ điều hành máy, mạng, hệ thống tệp và hơn thế nữa. LIBUV là một thư viện mã nguồn mở tập trung mạnh vào I / O không đồng bộ (Input-output). LIBUV được viết bằng C ++. Ngoài việc tập trung vào I/O không đồng bộ LIBUV còn triển khai hai tính năng quan trọng là event loop và thread pool.

Event Loop chịu trách nhiệm xử lý các tác vụ đơn giản hơn và Thread pool quản lý các tác vụ nặng. 

Event Loop

Khi chúng ta sử dụng Node.js trên máy tính, có nghĩa là có một tiến trình Node đang chạy trên máy tính đó. Quá trình này chỉ là một chương trình đang được thực thi. Bây giờ trong quá trình đó, Node.js chạy trong một thread (luồng) duy nhất. Một thread về cơ bản chỉ là một chuỗi các hướng dẫn và không cần thiết phải hiểu sâu về thread hoặc quy trình là gì. Chỉ cần tưởng tượng thread là một hộp nơi mã của chúng ta được thực thi trong bộ xử lý của máy tính. Bây giờ, điều cần thiết để hiểu ở đây là thực tế là Node chạy trong single thread, ví dụ, nếu chúng ta có 4 nhiệm vụ khác nhau thì tất cả bốn tác vụ này sẽ xảy ra trong một thread duy nhất.

Bạn có 1 người dùng hay 100 người dùng hoặc có thể 100 triệu người dùng truy cập ứng dụng của bạn cùng một lúc thì vẫn vậy. Event loop được gọi là trung tâm của node.js. Nó thực thi tất cả các hàm gọi lại (các hàm được gọi ngay khi một số công việc kết thúc) trong một luồng duy nhất và nó cũng giảm tải các tác vụ nặng hoặc tốn kém như nén tệp vào một thread pool. Eventloop giúp lập trình không đồng bộ có thể thực hiện được trong Node.js.

 

Nó xử lý tất cả các sự kiện đến và thực hiện phần cân bằng bằng cách giảm tải các tác vụ nặng hơn vào Thread Pool và tự thực hiện các tác vụ đơn giản hơn. Mình sẽ nói lại về Event Loop trong một vài bài viết tiếp theo vì có rất nhiều điều để nói về nó và nó là tính năng quan trọng nhất trong node.js. Bây giờ, bạn nên nhớ một điều rằng Event Loop là trái tim của Node js, nó làm cho Node hoàn toàn khác biệt với các ngôn ngữ Back end khác.

Thread pool

Thread pool cung cấp cho chúng ta 4 thread bổ sung hoàn toàn tách biệt với single thread trong Event Loop. Chúng ta có thể cấu hình nó lên đến 128 thread, nhưng thông thường, 4 thread này là đủ. Vì vậy, 4 thread này cùng nhau tạo thành một thread pool. Event loop sẽ tải bớt các tác vụ nặng vào Thread pool và điều này diễn ra tự động. Các DEV không phải là người quyết định cái gì đi vào Thread pool và cái gì không. Một số tác vụ tốn kém được giảm tải là : tất cả các hoạt động xử lý tệp, mọi thứ liên quan đến mật mã, như mật khẩu lưu vào bộ nhớ đệm, sau đó là tất cả các hoạt động liên quan đến nén, tra cứu DNS (khớp các miền web với địa chỉ IP thực tương ứng của chúng) và hơn thế nữa. Đó là những thứ dễ làm nghẽn luồng chính nhất. Vì vậy, Node sẽ xử lý nó bằng cách tự động tải chúng vào Thread pool.

Ví dụ- Ở đây khi yêu cầu truy cập vào máy chủ Node, nó sẽ chuyển đến Event loop thông qua hàng đợi. Bây giờ Event loop sẽ kiểm tra xem tác vụ có phải là tác vụ nặng như các thao tác xử lý tệp hay các thao tác liên quan đến mạng hay không. Nếu nhiệm vụ nặng, nó sẽ tải nó xuống Thread pool, các thread trong này sẽ thực thi tác vụ riêng biệt. nó sẽ không chặn Event loop của chúng tôi và để Event loop có thể xử lý các tác vụ đơn giản hơn.

Các thư viện quan trọng khác

Các thư viện quan trọng nhất cho Node.js là LIBUV và V8. Tuy nhiên, Node không chỉ hoạt động dựa trên V8 và LIBUV mà còn dựa trên một số thư viện khác như HTTP parser for parsing HTTP, C-ARES for DNS queries, OpenSSL for cryptography, and Zlib for file compression. Khi tất cả các thành phần này kết hợp hoàn hảo với nhau, Node.JS sẵn sàng được sử dụng ở phía máy chủ cho tất cả các ứng dụng.

Kiến trúc hướng sự kiện - Event-Driven Architecture

Hầu hết các mô-đun cốt lõi của Node, như HTTP, Hệ thống tệp được xây dựng dựa trên kiến ​​trúc hướng sự kiện. Khái niệm này thực sự khá đơn giản. Trong Node, có một số đối tượng nhất định được gọi là bộ phát sự kiện phát ra các sự kiện được đặt tên ngay khi có điều gì đó quan trọng xảy ra trong ứng dụng, chẳng hạn như yêu cầu truy cập máy chủ hoặc tệp hoàn tất để đọc. Sau đó, các sự kiện này được chọn bởi các trình nghe sự kiện đã thiết lập, điều này sẽ kích hoạt các chức năng (hàm gọi lại) được gắn với mỗi trình nghe.

Một mặt, chúng ta có các bộ phát sự kiện sẽ phát ra các sự kiện được đặt tên, và mặt kia, chúng ta có các bộ xử lý sự kiện phản ứng với các sự kiện được phát ra bằng cách gọi các hàm gọi lại. Kiến trúc hướng sự kiện giúp cho việc phản ứng nhiều lần với cùng một sự kiện trở nên dễ dàng hơn. Tất cả những gì chúng ta phải làm là thiết lập nhiều người nghe.
Ok, túm lại đây là kiến ​​trúc hướng sự kiện của Node. Đừng lo lắng nếu tất cả nghe có vẻ lý thuyết quá, mình sẽ giải thích thêm về các sự kiện và hàm gọi lại trong bài viết tiếp theo và chúng ta cũng sẽ thấy logic này được sử dụng trong nhiều tình huống. Vì vậy, hẹn gặp bạn trong bài viết tiếp theo nhé. Have a nice day!

Bài dịch từ Medium