Hiện nay nodejs đang nổi lên là ngôn ngữ server rất hot.

Nodejs là ngôn ngữ javascript chạy trên server, có ưu điểm là thực thi nhanh, bất đồng bộ.

1. Non-blocking I/O

Trong javascript, hầu hết các lời gọi I/O đều là non-blocking. Nghĩa là khi có HTTP request, truy xuất dữ liệu trong DB hoặc đọc ghi vào bộ nhớ thì hệ thống sẽ không tạm dừng (blocking) các đoạn code tiếp theo (như các ngôn ngữ server khác PHP, Ryby,...) mà sẽ trao quyền thực thi những lời gọi I/O này cho hệ thống và thực thi những đoạn code tiếp theo, khi hệ thống đã thực thi xong những lời gọi hệ thống này thì hàm callback truyền vào sẽ tự động được gọi.


Ví dụ về blocking trong Ruby:

response = Faraday.get 'https://blog.daovanhung.com'
puts response
puts 'Done!'

Trình tự hệ thống thực hiện đoạn code trên là:
 - Request tới https://blog.daovanhung.com, lúc này hệ thống sẽ tạm dừng (blocking) để chờ kết qủa của request này trả về.

 - Thông tin trả về và được gán vào biến response.

 - Hiển thị response được trả về lên màn hình.

 - Hiển thị 'Done' lên màn hình.

Thứ tự thực hiển của các ngôn ngữ server thông thường (như PHP, Ruby) sẽ thực hiện kiểu synchronous (đồng bộ) từ trên xuống dưới như flow trên. Còn dưới đây là ví dụ về trình tự thực thi trong nodejs(javascript):

request('https://blog.daovanhung.com', function(error, response, body) {
  console.log(body);
});

console.log('Done!');

 Lưu ý: Đoạn code trên cũng sẽ được gọi theo thứ tự từ trên xuồng dưới.

 - Câu lệnh đầu tiên sẽ gọi request tới https://blog.daovanhung.com, không như trên, ở đây hệ thống sẽ không đợi kết qủa trả về mới thực thi tiếp. Hệ thống sẽ truyền hàm callback ở trên vào event loop để khi hàm request có kết qủa trả về thì sẽ thực thi hàm callback này ngay. Sau khi truyền hàm callback vào event loop thì hệ thống sẽ tiếp tục thực hiện câu lệnh phía dưới.

 - In ra màn hình 'Done!'

 - Vào thời điểm nào đó sau đó, khi request có kết qủa trả về thì hàm callback sẽ được gọi và in ra body của response.

Code như trên gọi là assynchronous (bất đồng bộ), nghĩa là hệ thống có thể thực hiện các đoạn code của mình một cách đồng thời(như vd trên Done! được in ra trước body trả về).

 

2.Event Loop.

Nodejs giúp code của bạn chạy những đoạn còn lại trong khi vẫn chờ response trả về để thực thi hàm callback truyền vào. Thế nhưng câu hỏi đặt ra là hàm callback này được lưu trữ ở đâu, chúng được thực thi theo thứ tự nào khi có nhiều hàm callback như thế, và cái gì gọi chúng thực thi.

Trình khởi chạy của javascript có một hàng đợi (queue) chứa các messages, các messages này được gắn liền với các hàm callback truyền vào. Mỗi khi gặp câu lệnh có callback truyền vào thì message gắn với callback đó sẽ được đẩy vào queue, và khi các event được trigger( ví dụ như event click, event request có response trả về) thì hệ thống sẽ gọi hàm callback tương ứng để thực thi. Khi một event ví dụ như click vào button nhưng không chuyền vào callback thì sẽ không có message nào được đẩy vào queue, nghĩa là chỉ những event có callback tryền vào thì mới được đẩy vào queue.

 

Hình trên mô tả hoạt động của Event Loop. Khi có trigger(sự kiện click hoặc request ở vd trên thực hiện xong và có response trả về) thì hàm callback truyền vào sẽ được đẩy vào Massage Queue.
Khi biên dịch đoạn code ban đầu thì các dòng lệnh assyn của bạn sẽ được đẩy vào stack, khi các dòng lệnh này chạy từ đầu tới cuối (chúng chạy song song với các tiến trình truy cập I/O nói ở trên) và khi chạy xong câu lệnh cuối cùng rồi thì stack sẽ về rỗng. Một khi stack về rỗng (chạy xong thân code của bạn), vòng lặp Event Loop sẽ được khởi chạy. Mỗi khi Event Loop gặp một message trong Message Queue, nó sẽ thực thi hàm callback gắn với message đó bằng cách đẩy các đoạn code trong hàm callback vào stack. Sau khi hàm callback đó thực hiện xong, stack về rỗng, thì Event Loop tiếp tục chạy và lấy message tiếp theo (nếu có) ra và đẩy code của callback vào stack thực thi.

 

3. Nodejs xử lý nhiều request cùng lúc như thế nào?

Như giải thích ở phần 2, nodejs là single thread non-blocking (hiểu thêm về thread ở bài giới thiệu thread từ phần cứng ra phần mềm) nên các request tới sau sẽ được thực hiện ngay sau khi thân code của request thứ nhất được thực hiện xong, thân code ở đây là mọi thứ đang có trong call stack và message queue, những callback nằm trong message queue là những callback đã có kết quả trả về, còn những callback thuộc về request thứ nhất mà chưa có kết quả trả về sẽ chưa được đẩy vào message queue.

Chú ý: Không dùng biến global trong method của http request

Vì:

Giả sử có 2 request là request 1 và request 2, request 1 có 2 callback là callback A và callback B. Thứ tự các request và callback được đẩy vào message queue là request 1 => callbackA => request 2 => callback B.

Trong trường hợp nếu callback B muốn truy cập vào biến global nhưng biến global này lại bị request 2 thay đổi (request 2 luôn được thực hiện trước callback B) thì callback B sẽ truy cập sai dữ liệu dự kiến và khiến cho request 1 trả về kết quả sai.

          nodejs xử lý nhiều request cùng lúc như thế nào

 

Kết luận rút ra

 - Các lời gọi I/O là assync (nhiều I/O được thực hiện cùng 1 lúc).

 - Các lời gọi hàm của bạn là sync (Code của bạn chạy tuần tự từ trên xuống dưới).

 - Các hàm callback sẽ chạy sau khi thân code của bạn thực thi xong.

 - Các hàm callback sẽ chạy tuần tự theo thứ tự callback nào được trigger trước sẽ chạy trước chứ không phải các hàm callback được chạy đồng thời.

=> Đây chính là single thread trong nodejs. Nghĩa là chỉ có một tiến trình được chạy trong code của bạn (Còn những lời gọi I/O assyn là của hệ thống gọi multi thread).