NodeJS – Single threaded Model

Khi mới bắt đầu tìm hiểu Nodejs, có một câu hỏi phổ biến liên quan đến cơ chế hoạt động bên dưới Nodejs được đặt ra cho người mới bắt đầu:

Tại sao Nodejs lại chỉ dùng 1 thread và được kỳ vọng sẽ có hiệu năng cao hơn các nền tảng sử dụng đa luồng (multi-thread) ? Chẳng phải đa luồng thì sẽ xử lý cùng lúc được nhiều việc hơn sao.

Cùng tìm hiểu lý giải phù hợp cho câu hỏi này.

  • Trước hết cùng tìm hiểu cách các ứng dụng đa luồng handle request:

    Mỗi khi có 1 request tới, app sẽ sinh ra 1 thread để xử lý request được. Thread này sẽ chạy logic code, gọi các I/O event (tương tác DB, file, network …), đợi kết quả, và trả về cho client. Vì mỗi request được handle bởi 1 request, kiến trúc này thường được gọi là one thread per request model. Hầu hết các webserver Java sử dụng mô hình này.

    Điểm yếu của mô hình này:

    • Khi số lượng request tăng lên, số thread cũng tăng lên => ngốn RAM
    • Mỗi thread khi handle request liên quan đến I/O, trong khi đợi kết quả từ I/O thì ở trong trạng thái rảnh rỗi => lãng phí tài nguyên.

      Related image

  • Mô hình single-threaded

    Trong mô hình này, khi nhận request, main-thread sẽ xử lý và gọi các I/O event, sau đó tiếp tục nhận các request khác mà không chờ đợi kết quả từ I/O event => Chúng ta tránh được các điểm yếu của mô hình one thread per request.

    Image result for multi threaded vs single threaded

  • Trở lại với câu hỏi ở đầu bài, ta có thể nhận thấy mặc dù Nodejs là single thread nhưng không phải mọi tác vụ đều được xử lý tại thread này. Ví dụ khi request cần gọi vào database, process riêng của database sẽ xử lý việc gọi vào database, và rõ ràng là process của database này là multi-thread. Nói cách khác, main-thread của Nodejs sẽ ủy quyền đặc tính multi-thread cho các process khác. Trong thực tế, mọi tác vụ bất đồng bộ đều được main-thread ủy quyền cho các thread của process khác hoặc thread trong thread-pool (gồm mặc định 4 threads) của libuv.

    Từ luận điểm trên ta có thể suy ra điểm yếu của mô hình single-thread, đó là các tác vụ đồng bộ cần nhiều CPU (Fourier transform, 3D rendering) đều được xử lý ở main-thread => nó sẽ busy và không thể nhân thêm request mới => block cả app. Ngoài ra nếu máy tính có nhiều core thì single thread cũng chỉ tận dụng được 1 core.

Bạn hiểu Nodejs tới mức nào?

  1. V8 là gì? Node có chạy được không nếu thiếu V8?
    – V8 là một javascript engine dùng để compile, tối ưu và chạy Node.js
    – Node có thể chạy mà không cần V8, một vài js engine khác: SpiderMonkeyChakraCore
  2. exports và module.exports
    enter image description here
    Ban đầu exports và module.exports cùng trỏ vào 1 object rỗng. Nhưng hãy luôn nhớ rằng, ở cuối mỗi module trong Nodejs, module.exports là object được return.
    – Khi bạn code như sau:

    Nodejs sẽ add thêm 1 thuộc tính a và b vào object rỗng ban đầu. Do exports và module.exports vẫn đang trỏ vào cùng 1 object nên sẽ không có sự khác biệt khi bạn code:

    – Khi bạn code như sau:

    Nodejs sẽ báo lỗi ngay lập tức. Bởi vì exports giờ đã được trỏ tới 1 object hoàn toàn mới (function Something), trong khi module này vẫn return exports.module

    Tips: chỉ dùng exports.module cho đỡ phải nhọc não

  3. Khi khai báo 1 biến global trong 1 module, tại sao nó không global với các module khác?
    Do mỗi module trong Nodejs được wrap bởi 1 function:

  4. Có thể dùng nhiều version của 1 package trong cùng 1 app ko?
    Không
  5. Event-loop là gì?
    <
    wait-for-it>
  6. Call-stack là gì?
    Là 1 cấu trúc dữ liệu stack ghi lại các lời gọi hàm:Khi 1 hàm được gọi, nó được push vào stack. Khi 1 hàm return, nó được pop khỏi stack.Mỗi khi chương trình gặp lỗi, browser sẽ hiển thị trạng thái hiện tại của call-stack. Trong Node, call-stack được xử lý bởi V8.
  7. Sự khác nhau giữa spawn, exec, và fork
  8. Xem memory used by Node?

  9.  V8 Object là gì?
    La object C++ native biểu diễn object JS. Nói cách khác V8 object là cách V8. biểu diễn object của JS.
  10. Libuv là gì?
    libuvMột thư viện đa nền tảng tập trung vào xử lý các tác vụ I/O bất đồng bộ: file system event, event loop, thread pool, timers…
  11. Làm sao để thực hiện 1 function trước khi exit process?

  12. Node buffer có sử dụng V8 memory ko?
    Không. Node buffer mem được allocated bên ngoài V8 heap.
  13. Trường main trong file package.json có tác dụng gì?

    Xác định entry point khi app được chạy. Ví dụ khi app với package.json trên được chạy, nó sẽ chạy vào file index.js đầu tiên.

  14. 3 loại file sẽ được chạy khi require
    .json, .node, .js
  15. Bằng cách nào 1 module có thể được gọi trực tiếp và require từ module khác đồng thời?

  16. Đo execute time ntn?

  17. Callback là gì?
    Là 1 hàm được gọi sau khi 1 tác vụ được hoàn thành.
  18. Error-first callback là gì?
    Là 1 callback với argument đầu tiên luôn là error, các argument khác được dùng để pass data.

  19. Promise là gì?
    Là 1 cơ chế giúp xử lý các tác vụ bất đồng bộ.