Bắt đầu với NodeJS - The Event Loop, and Non-Blocking Asynchronous Execution (Phần 4)

I. Tác vụ hệ thống và tác vụ đặc trưng.



- Như các bạn đã biết mã nguồn của NodeJS bao gồm các thư việc và các module phụ thuộc để giúp chúng ta viết mã js phía server.
- Những thư viện và module phụ thuộc này có thể viết bằng C++ hoặc js tùy vào từng công dụng của nó đối với những tác vụ đọc , ghi , xóa file tương tác nhiều với HĐH thì nhưng module này sẽ viết bằng C++ để tăng tốc độ , chúng ta có thể hiểu đơn giản rằng JavaScript không được thiết kế ra để xử lý ở cấp độ HĐH nên nó phải nhờ đến C++.

- Những tác vụ liên quan đến HĐH thực sự được viết  bằng một thư viện C++ gọi là libuv , thư viện này như bên trên viết để đối phó với các sự kiện tương tác nhiều với HĐH.
Ví dụ như có sự kiện xảy ra như dowload cái gì đó từ internet về hoặc mở một tệp tin (đọc , ghi file)


- Giờ hãy tưởng tượng :
=> Trong Node chúng ta có V8 và V8 dịch code JS sang mã máy , điều chúng ta cần nhờ là JS chạy đồng bộ nó chạy từ trên xuống dưới đợi tác vụ này xong mới đến tác vụ khác suy ra V8 thực thi một hành động tại một thời điểm nhất định

=> Trong Node cũng có một thành phần nữa là Libuv là một thư viện mà ở bên trên mình đã nói nó được viết riêng để đối phó với những sự kiện hay nói đúng hơn là các tác vụ tương tác nhiều với HDDH (đọc , ghi file chẳng hạn).


=> Libuv kết nối đến OS bằng cách gửi một yêu cầu đến OS (ví dụ như dowload một tệp tin từ trên internet hay mở một tệp tin). Trong libuv chúng ta cần để ý đến 2 thành phần quan trọng của Libuv đó là EventLoop (Vòng lặp sự kiện) và Queue(Hàng đợi).
+ Trong Queue đặt các sự kiện đã được xử lý xong (done)
+ Thành phần quan trọng nhất trong Libuv là  EventLoop , nó có nhiệm vụ liên tục kiểm tra xem cái gì đã diễn ra , kiểm tra xem CallStack trống hay chưa ,
(Tất cả những điều này (libuv kết nối tới OS , các sự kiện OS giải quyết xong r đẩy vào QUEUE hay EvevntLoop kiểm tra xem cái gì đã xảy ra)  xảy ra đồng thời khi V8 đang chạy , chúng ta có thể rõ ràng đây là sự bất đồng bộ của Node , chạy nhiều tác vụ một lúc)


=> Nhưng tại một số thời điểm có thể HĐH sẽ một thời gian tương đối lâu để giải quyết tác vụ .
và lúc này các tác vụ sẽ được OS chuyển vào trong QUEUE và tất nhiên có thể có nhiều hơn một tác vụ đòi hỏi HĐH mất một khoảng thời gian để thực hiện , Vòng lặp liên tục kiểm tra QUEUE . Nếu có sự kiện nào mới được thêm vào QUEUE (tức là đã hoàn thành) , lúc này Libuv sẽ thấy sự kiện nào đã hoàn thành nó sẽ xử lý event đó và nó sẽ chạy một callback => Đó là nói mã được chạy khi event này hoàn thành kiểu như Lập trình viên code và điểu khiển  Libuv rằng đây là đoạn code nên chạy khi đoạn code khác chạy xong.


=> Như trên mình đã nói JS là đồng bộ nó không bất đồng bộ V8 chỉ thực hiện xử lý một tác vụ tại một thời điểm , mã JS của tôi đang chạy và nó gặp một sự kiện phải tương tác nhiều với HĐH , libuv kết nối tới OS bằng một yêu cầu tương ứng với sự kiện , OS xử lý xong và đẩy nó vào QUEUE , EventLoop kiểm tra và thấy nó đã hoàn thành và bảo V8 thực hiện sự kiện đó nhưng V8 phải đợi để thực hiện sự kiện đó.
- Suy ra JS vẫn chạy đồng bộ nhưng toàn bộ quá trình này thì không đồng bộ bởi vì có những tác vụ xảy ra trong libuv và V8 cùng một lúc.


II. Event Driven Non - Blocking I/O

- Khi nói về Node bạn thường nghe về những thứ giống như Event Drive Non-Blocking I/O
trong V8 . Như mình đã trình bày V8 được nhúng bên trong Node và nó thực thi mã JS .
Khi nói về I/O (Input/Output) đây là những gì diễn ra ở cấp HĐH như mở tệp tin , kết nối đến db lấy và gửi thông tin đến Internet nhưng tác vụ này xảy ra trong hệ thống máy tính của bạn và trong máy chủ .
vì vậy phần EventDriven(hướng sự kiện) là nơi chúng ta gửi request đến server những điều này xảy ra và sau đó chúng ta nhận được thông báo tác vụ khi đã hoàn thành nhưng được task này trong hàng đợi (QUEUE) sự kiện xảy ra và vòng lặp liên tục kiểm tra xem CallStack đã trống hay chưa và CallBackQueue có callback nào được đẩy vào hay không ?

=> Bởi vì mã  JS của bạn có thể tiếp tục chạy khi những tác vụ (System Event) diễn ra điều này được gọi là Non-Blocking , hiểu đơn giản Non-Blocking là làm những việc khác mà không dừng lại luồng chạy của chương trình điều này được thực hiện bởi Node đang làm mọi thứ không đồng bộ 

- Một BlockingProcess có thể hiểu đơn giản qua ví dụ là
   Hey OS hãy lấy cho sếp tập tin này => Sếp sẽ đợi cho đến khi toàn bộ tệp tin được đọc và tất cả nội dung được load vào bộ nhớ đệm => Sếp chuyển qua dòng code tiếp theo (Chương trình của bạn bị dừng khi đợi hoàn thành một tác vụ nào đó)

=> Vấn đề phát sinh khi thực hiện chương trình theo kiểu này là đôi khi tác vụ nặng , mạng Internet lởm chẳng hạn -_- như vậy chương trình của bạn sẽ bị dừng.

=> Nhưng với cách tiếp cận Non-Blocking của Node thì DEV yêu cầu Node thực hiện những tác vụ khác trong khi OS xử lý sự kiện và tất nhiên mã JS tiếp tục chạy => Chương trình vẫn chạy bình thường , điều này cung cấp cho Node một khả năng xử lý rất mạnh mẽ vì nó cho phép rất nhiều người có thể gửi request đồng thời .

- Async không phải là cái gì mới , tư duy của nó đã tồn tại từ rất lâu , ý tưởng về một máy tính làm nhiều việc một lúc thật tuyệt vời nhưng nó rất khó để thực hiện ở góc độ lập trình vì nó rất khó để quản lý các case.


=> Node cho phép chúng ta viết mã sync để dễ quản lý nhưng vẫn có thể viết mã async , như vậy nó giảm bớt những gì phải giải quyết đồng thời.
Node nói rằng : Hãy thực hiện tác vụ này khi tác vụ xong trong khi đó tôi sẽ tiếp tục chạy các dòng khác.
Chính vì vậy người ta mới nói Node có hiện suất cao nó là là Non-Blocking nó không bị chặn nó có thể tiếp nhận nhiều request một lúc và nó không bị chặn , tiếp tục chạy mã JS

III. V8 in Node vs V8 in Chrome.

- Có thể các bạn từng thắc mắc V8 tích hợp trong Node thì nó khác gì V8 thích hợp trong Chrome
=> Sự khác biệt lớn nhất đến từ Libuv trong Node còn Chrome không có điều này.

- Libuv là một thư viện độc lập được viết bằng C++ sử dụng chủ yếu cho Node nhưng có thể được sử dụng ở nơi khác.

=> Để chứng mình rằng trong Libuv có vòng EventLoop chúng ta có thể nhìn vào trong Source của Libuv và thấy
Vòng lặp liên tục kiểm tra các Event được hoàn thành và sau đó trả kết quả của sự kiện này dưới dạng CallBack , đẩy các CallBack này vào trong CallBackQueue .

Nhận xét

Bài đăng phổ biến