Để tạo ra một ứng dụng với NodeJS có thể chúng ta chỉ cần biết đến các module core hoặc các thư viện mà cộng đồng cũng cấp là đủ. Tuy nhiên để làm chủ được NodeJS có lẽ chúng ta nên hiểu sâu một chút về kiến trúc phần mềm của NodeJS.
Kiến trúc cơ bản
Không quá khó để chúng ta có thể hình dung được kiến trúc phần mềm cơ bản của NodeJS. Chúng ta viết một chương trình bằng ngôn ngữ Javascript, vậy thì phải có một thứ gì đó kiểu thư viện hay hàm hay chương trình con được nhúng trong NodeJS để thông dịch mã nguồn Javascript.
Khoan đã, dừng lại khoảng chừng là 3 giây. Vậy thì thông dịch ở đây là thông dịch ra cái gì? Đó chính là việc thông dịch từ các câu lệnh được viết bằng javascript sang các câu hàm được viết bằng ngôn ngữ native ví dụ C/C++. Các hàm native này sẽ được thực thi bởi CPU hoặc System call của hệ điều hành.
Như vậy chúng ta có thể dựng được một kiến trúc cơ bản của NodeJS như sau:
Lưu ý rằng Ứng dụng viết bằng Javascript là mã nguồn do lập trình viên viết và không thuộc kiến trúc phần mềm của NodeJS.
Kiến trúc chi tiết hơn
Kiến trúc cơ bản đã tương đối dễ hiểu, tuy nhiên chúng ta hãy đào sâu hơn một chút nữa. Trong những bài trước mình đã nói về một vài khái niệm như main thread hay đã có những ví dụ sử dụng bất đồng bộ, đây cũng chính là một trong những điểm nhấn của NodeJS nói riêng hay các ngôn ngữ lập trình scripts nói chung. Bằng cách sử dụng mô hình bất đồng bộ, NodeJS sẽ giúp cho lập trình viên không còn phải quan tâm đến lập trình đa luồng vốn dĩ đã quá phức tạp với phần lớn mọi người, hay nói cách khác là đánh đổi khả năng tối ưu để lấy sự tiện lợi cho lập trình viên.
Bất đồng bộ trên thực tế được cài đặt bởi event loop, và một event loop được đặc trưng bởi một hoặc nhiều thread, một queue, một vòng lặp while true và hàm callback.
Callback mà chúng ta vừa nói đến sẽ được ánh xạ đến hàm xử lý ở javascript. Nếu viết bằng giả mã thì nó sẽ kiểu thế này:
class EventLoop {
Queue<Function> queue = new Queue<>();
public void start() {
Thread newThread = new Thread(() => {
while(true) {
while(queue.isNotEmpty()) {
Function function = queue.poll();
function.apply();
if (function.callback) {
function.callback();
}
}
Thread.sleep(3); // 3 milli giây
}
});
newThread.start();
}
}
Từ đây chúng ta có thể vẽ được kiến trúc chi tiết hơn của NodeJS như sau:
Ở đây chúng ta có:
- Ứng dụng viết bằng Javascript là mã nguồn do lập trình viên viết và không thuộc kiến trúc phần mềm của NodeJS.
- Trình thông dịch Javascript hiện tại NodeJS đang sử dụng là V8 JavaScript Engine.
- Chứa danh sách ánh xạ giữa các hàm javascript và hàm native, mã nguồn của nó sẽ kiểu thế này. Ở bước này thì các hàm có thể được thực thi ngay nếu ở chế độ đồng bộ hoá, nghĩa là các hàm native có thể được gọi và sẽ được thực thi bởi CPU hoặc system call của hệ điều hành.
- Với các hàm bất đồng bộ thì sẽ được đưa vào trong hàng đợi để được thực thi sau.
- Các I/O thread cũng có thể sở hữu riêng các event loop của nó và đẩy các hàm kết quả vào cho hàng đợi, bạn có thể tham khảo mã nguồn cho ý tưởng này tại đây.
- Event Loop được duy trì chạy liên tục bởi Main Thread để lấy các hàm ra khỏi queue và thực thi lần lượt.
- Trong quá trình thực hiện thì các I/O thread hay main thread có thể gọi thực thi các hàm native, các hàm native sẽ được thực thi bởi phần cứng máy tính hoặc system call của hệ điều hành.
Tổng kết
Như vậy chúng ta đã cùng nhau tìm hiểu về kiến trúc phần mềm của NodeJS. Bạn có thể tham khảo mã nguồn của NodeJS tại https://github.com/nodejs/node hoặc tham khảo mã nguồn ở một quy mô nhỏ hơn thông qua dự án ezyfox-server-react-native-client nhé.
Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:
- Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
- Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
- Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a
Bình luận