Được thiết kế cho các nhà phát triển ở ngã tư của sự tò mò và sự phát triển chuyên nghiệp, chủ đề này nhằm mục đích làm sáng tỏ cách lập trình không đồng bộ có thể nâng cao hiệu suất ứng dụng, khả năng mở rộng và khả năng đáp ứng.Bằng cách tập trung vào các nguyên tắc phổ quát của các hoạt động không chặn và vòng tròn sự kiện, chúng tôi mạo hiểm vượt ra ngoài các công nghệ cụ thể như AsyncIO, Node.js, hoặc Go.
Nghiên cứu này được thiết kế cho các nhà phát triển phần mềm khao khát nắm bắt hiệu quả điều khiển phần mềm hiện đại mà không cần sự phức tạp của jargon kỹ thuật.
Hãy nghĩ về cách một yêu cầu xử lý dây duy nhất giống như một con đường duy nhất trong một thành phố bận rộn: mỗi yêu cầu là một chiếc xe, và khi chỉ có một con đường, xe hơi sắp xếp lại, gây ra sự chậm trễ.Nhưng nếu thành phố của chúng tôi - dịch vụ của chúng tôi - có thể quản lý giao thông một cách khôn ngoan hơn?Đó là nơi phép thuật của thời gian chạy không đồng bộ bắt đầu hoạt động.Nó giống như thêm một hệ thống giao thông thông minh vào thành phố của chúng tôi, hướng dẫn xe qua nhiều con đường và giao thông mà không cần chờ đợi.
Hệ thống này giữ lưu lượng truy cập chảy suôn sẻ, đảm bảo không có chiếc xe (hoặc nhiệm vụ) chờ đợi quá lâu, đó chính xác là cách chạy thời gian không đồng bộ giữ cho phần mềm của chúng tôi chạy hiệu quả.
Hãy xem xét một ví dụ về một dịch vụ hoàn thành các hoạt động I/O đồng bộ.Để rõ ràng, các hoạt động này được hiển thị bên ngoài luồng thực hiện chính trong biểu đồ:
Block I/O trong giai đoạn khởi động dịch vụ của bạn có thể được chấp nhận, nhưng nên tránh cách tiếp cận này khi xử lý yêu cầu bên ngoài.
Những ví dụ này làm nổi bật các tình huống mà hiệu suất của máy chủ được tối đa hóa. Tuy nhiên, lợi thế của các hoạt động không chặn vẫn tồn tại trên bất kỳ tần số nào của các yêu cầu đến. Ngay cả trong điều kiện ít hơn lý tưởng, hiệu suất được hưởng lợi từ việc tích hợp các chủ đề I/O chuyên dụng vào dòng công việc xử lý.
Tổng thời gian cần thiết để xử lý một yêu cầu (từ yêu cầu ban đầu của khách hàng đến câu trả lời cuối cùng, như được mô tả bởi số màu xanh ở bên phải) sẽ luôn giảm, miễn là có đủ chủ đề để xử lý tất cả các yêu cầu.
Chúng tôi sau đó gặp phải một thực tiễn có vẻ không trực quan đối với nhiều nhà phát triển.Khi các hoạt động I/O chiếm một phần đáng kể thời gian xử lý yêu cầu, việc tối ưu hóa các phân đoạn mã khác có thể mang lại sự cải thiện nhỏ.Thời gian để lấy dữ liệu từ bộ nhớ cache có thể phù hợp chặt chẽ với thời gian dành cho logic kinh doanh và hiển thị mẫu.
Để phân đoạn mã một cách hiệu quả và tạo điều kiện cho việc thực hiện cuộc gọi trở lại, người ta có thể chỉ thị thời gian chạy để chuyển sang chu kỳ vòng tròn sự kiện tiếp theo.Dưới đây là một ví dụ minh họa về cách khái niệm này được áp dụng:
// chặn chức năng callbacks func1_cb(str, cb) { var res = func1(str); cb(res); } function func2_cb(str, cb) { var res = func2(str); cb(res); } // non-blocking callbacks function func1_cb(str, cb) { var res = func1(str); process.nextTick(function () { cb(res); } } function func2_cb(str, cb) { var res = func2(str); process.nextick(function () { cb(res); }); } // sử dụng ví dụ function1_cb(content, function (str) function {2_cb(str, function (result { // work with result } } }); }
// blocking callbacks function func1_cb(str, cb) { var res = func1(str); cb(res); } function func2_cb(str, cb) { var res = func2(str); cb(res); } // non-blocking callbacks function func1_cb(str, cb) { var res = func1(str); process.nextTick(function () { cb(res); }); } function func2_cb(str, cb) { var res = func2(str); process.nextTick(function () { cb(res); }); } // usage example func1_cb(content, function (str) { func2_cb(str, function (result) { // work with result }); });Bằng cách áp dụng phương pháp này để tách hai phần tính toán, tổng thời gian xử lý cho các kịch bản có yêu cầu đến gần như đồng thời vẫn giữ nguyên.
Kịch bản này đại diện cho kết quả ít thuận lợi nhất khi sử dụng chiến lược “next tick”. Như được thể hiện trong ví dụ ban đầu, phương pháp “next tick” được chứng minh là lành tính đối với các yêu cầu hiếm gặp.Nếu các yêu cầu đến với tốc độ vừa phải, việc tận dụng kỹ thuật này sẽ tăng tốc độ xử lý bằng cách cho phép bắt đầu các yêu cầu mới và bắt đầu các hoạt động không chặn được nối lại trong thời gian ngừng thực hiện.Phương pháp này có hiệu quả giảm cả thời gian xử lý tổng thể và thời gian trung bình cho mỗi yêu cầu:
Để kết luận, việc áp dụng I/O không chặn là rất quan trọng để cải thiện hiệu suất ứng dụng và có lợi trong các môi trường có khối lượng yêu cầu đến ít và nặng.Thêm vào đó, theo dõi hiệu quả luồng thực hiện - được minh họa bởi các khái niệm tương tự như kỹ thuật "next tick" - cải thiện đáng kể hiệu quả máy chủ.