Là một lập trình viên Node.js đã bao giờ bạn nghe đến thế mạnh của nó là xử lý các tác vụ I/O và không đồng bộ? Rằng Node.js không phải là lựa chọn tốt cho các ứng dụng thiên về khai thác triệt để sức mạnh của CPU? Vậy thì tác vụ I/O là gì và tại sao Node.js lại mạnh về I/O? Việc nói Node.js không thực sự tốt với các phép tính lớn là có đúng hay không? Hãy cùng nhau tìm hiểu trong bài viết ngày hôm nay nhé!
I/O (Input/Output) đề cập đến sự tương tác của máy tính hay chương trình máy tính với ổ đĩa (disk) và mạng của hệ thống. Ví dụ các hoạt động I/O bao gồm đọc/ghi dữ liệu từ ổ đĩa, thực hiện các yêu cầu HTTP và tương tác với cơ sở dữ liệu. Chúng rất chậm so với việc truy cập bộ nhớ RAM hoặc các phép tính được thực hiện trên CPU.
Chúng ta biết rằng Node.js sử dụng V8 của Chrome để thực thi mã JavaScript, có điều sức mạnh của V8 cũng phải chào thua trước I/O bởi chúng không hoàn toàn phụ thuộc vào tốc độ CPU. Các tác vụ như đọc/ghi ổ cứng, truy cập bộ nhớ ngẫu nhiên (RAM), tốc độ mạng... nếu đưa vào xử lý trên V8 thì nó sẽ gây ra một cuộc tắc nghẽn nghiêm trọng vì tốn quá nhiều thời gian để chờ đợi. Chính vì thế Node.js phải tìm ra cách nào đó để vừa tận dụng được sức mạnh của V8 mà vẫn phải xử lý được I/O.
Giải pháp của Node là dùng libuv để xử lý I/O không đồng bộ. Đây là thư viện C đa nền tảng hỗ trợ I/O không đồng bộ dựa trên vòng lặp sự kiện (Event Loop).
Nói về quá trình xử lý. Luồng chính khi gặp các tác vụ I/O đẩy chúng xuống libuv, sau khi xử lý xong, kết quả được đưa trở lại luồng chính thông qua Event Loop. Cứ như thế luồng chính không phải chờ đợi I/O nào mà chỉ xử lý kết quả của I/O.
Node.js có khái niệm worker, chúng có nhiệm vụ chuyển các yêu cầu I/O từ luồng chính đến libuv. Trong thời gian đó chúng không phải làm gì khác và có thể bị hệ điều hành huỷ lập lịch (de-scheduled) để cho một worker khác gửi yêu cầu. Do đó các tác vụ I/O được worker chuyển vào từ trước vẫn được xử lý ngay cả khi luồng liên kết không chạy.
Hệ điều hành đã tối ưu các công cụ quản lý tệp và cơ sở dữ liệu cũng được tối ưu hoá cao để xử lý đồng thời nhiều yêu cầu chờ xử lý. Ví dụ như sắp xếp lại thứ tự ưu tiên khi có đồng thời nhiều yêu cầu đọc/ghi dữ liệu vào một tệp.
Khi chạy một ứng dụng Node.js bạn sẽ có một số thread pools chuyên dùng để xử lý các yêu cầu I/O. Nhóm luồng này được tạo bởi libuv. Số lượng mặc định của nó là 4 nhưng có thể tăng lên tối đa 128 thông qua biến môi trường UV_THREADPOOL_SIZE
.
Là những công việc đòi hỏi nhiều về khả năng tính toán của CPU. Đó có thể là những phép tính phức tạp về mã hoá/giải mã, xử lý hình ảnh, xử lý video... Các worker cũng có thể chuyển những yêu cầu tính toán phức tạp này, lập lịch và xử lý chúng bên ngoài luồng chính. Nhưng chúng chỉ được xử lý khi worker được lập lịch trên một trong các lõi của CPU. Ví dụ nếu CPU của bạn có 4 lõi và bạn tạo ra 5 worker thì một trong những worker này không được tham gia xử lý, trong khi vẫn phải duy trì một nguồn tài nguyên cho nó (bộ nhớ & lập lịch) gây ra tình trạng lãng phí tài nguyên.
Để hiểu rõ hơn về cách Node.js xử lý công việc tính toán nặng thông qua Worker Threads tôi khuyên bạn nên đọc bài Worker threads là gì? Bạn đã biết khi nào thì sử dụng Worker threads trong node.js chưa?.
Có thể thấy nếu như luồng chính đưa các tác vụ I/O ra libuv, nó sẽ ngăn chặn được một cuộc tắc nghẽn nghiêm trọng. Trong khi đó, các tác vụ thiên về CPU suy cho cùng sẽ chiếm thời gian xử lý. Giải pháp là tạo ra các child process hoặc worker threads nhưng tạo thế nào và tạo bao nhiêu còn phụ thuộc vào sức mạnh của phần cứng.
Mô hình kiến trúc của Node.js được thiết kế để tận dụng sức mạnh của V8 mà vẫn giải quyết được các tác vụ I/O. Node.js hoàn toàn phù hợp với những bài toán thiên về I/O. Nhưng không phải là Node.js không xử lý được bài toán khác thiên về sức mạnh của CPU. Để tránh chặn luồng chính hãy tạo ra child process hoặc worker threads để xử lý chúng trong một luồng riêng biệt.
Tôi & khao khát "chơi chữ"
Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên ngay!
Đăng ký nhận thông báo bài viết mới
Bình luận (1)