Thiết Kế Mới Cho Phân Phối Tin Nhắn Và Lập Lịch Dịch Vụ Trong Skynet - nói dối e blog

Thiết Kế Mới Cho Phân Phối Tin Nhắn Và Lập Lịch Dịch Vụ Trong Skynet

Vào tháng này, phiên bản 1.0 chính thức của skynet sẽ được phát hành. Bên cạnh việc duy trì phiên bản ổn định này, tôi đang cân nhắc thực hiện những thay đổi mạnh mẽ cho một số điểm còn chưa hài lòng (dĩ nhiên sẽ không áp dụng trên phiên bản ổn định hiện tại).

Vấn đề cốt lõi của skynet - cơ chế lập lịch đa dịch vụ và lan truyền tin nhắn nội bộ - chưa đạt được mức hiệu quả mong muốn. Tôi cho rằng nếu tiếp cận theo một hướng khác hoàn toàn có thể tối ưu hơn. Dưới đây là bản ghi chú chi tiết các ý tưởng mới này.

Hiện tại, mỗi dịch vụ đều có một hàng đợi tin nhắn duy nhất, có khả năng mở rộng vô hạn miễn là bộ nhớ cho phép. Điều này khiến việc gửi tin nhắn đến một dịch vụ gần như không thể thất bại. Trong thực tế, chiều dài hàng đợi của phần lớn dịch vụ đơn lẻ thường không quá lớn, và mô hình sản xuất-tiêu thụ cũng không khuyến khích hàng đợi dài. Khi hàng đợi kéo dài chứng tỏ tốc độ tiêu thụ đang chậm hơn đáng kể so với tốc độ sản xuất, tình trạng này thường có chiều hướng xấu đi theo thời gian. Trong lịch sử đã từng xảy ra nhiều sự cố nghiêm trọng liên quan đến hiện tượng quá tải dịch vụ.

Dù skynet cung cấp phương pháp mqlen giúp người dùng kiểm tra độ dài hàng đợi tin nhắn, nhưng đây chỉ là giải pháp tạm thời. Tôi muốn thực hiện một thay đổi thiết kế mang tính đột phá để giải quyết tận gốc vấn đề này.

Giải pháp đề xuất là chuyển hàng đợi tin nhắn của mỗi dịch vụ thành cấu trúc vòng có độ dài cố định (khoảng 256 slot là đủ). Cấu trúc này đơn giản hơn nhiều để triển khai, đồng thời đảm bảo thao tác vào/ra hàng đợi không gây tranh giành tài nguyên. Vì việc xử lý ra khỏi hàng đợi chỉ xảy ra bên trong một dịch vụ duy nhất, không có hiện tượng bất đồng bộ nên không cần xử lý cạnh tranh. Chúng ta chỉ cần tập trung giải quyết việc tranh giành khi đưa tin nhắn vào hàng đợi.

Khi hàng đợi đầy hoặc đang có thao tác ghi dữ liệu, hệ thống sẽ coi hàng đợi đang “bận” và từ chối yêu cầu gửi mới mà không cần sử dụng khóa (lock). Trong trường hợp này, phía gửi có thể tự động lưu trữ tạm thời dữ liệu chờ gửi. Đối với nhiều phía cùng ghi vào hàng đợi, skynet không cần đảm bảo thứ tự tuyệt đối - chỉ cần duy trì thứ tự giữa một người gửi cụ thể với người nhận duy nhất. Khi hàng đợi không bận, thứ tự ghi vào không quan trọng; và trong cùng một dịch vụ có nhiều hàng đợi chờ gửi, thứ tự xử lý cũng không cần quá nghiêm ngặt.

Thay đổi này sẽ loại bỏ hoàn toàn hiện tượng cạnh tranh tài nguyên trong cơ chế truyền/nhận tin nhắn, đồng thời giải quyết triệt để vấn đề hiệu ứng “thảm họa tuyết lở” khi dịch vụ quá tải. Những dịch vụ bị chấm dứt khi đang có tin nhắn chưa gửi sẽ tự động loại bỏ các tin nhắn đó khỏi hàng đợi, tránh gây tắc nghẽn. Trong thiết kế hiện tại, các tin nhắn chưa xử lý sẽ tích tụ thành hàng đợi dài, gây lãng phí tài nguyên đặc biệt khi người dùng liên tục đăng nhập/đăng xuất.

Một điểm phức tạp hơn trong thiết kế mới là cơ chế đánh thức dịch vụ khi nào. Trong thiết kế hiện hành, một dịch vụ chỉ được đưa vào hàng đợi xử lý khi nhận tin nhắn mới và không nằm trong hàng đợi dịch vụ nóng. Với thay đổi mới, khi dịch vụ có tin nhắn chờ gửi bị nghẽn, chúng ta cần cơ chế đánh thức tương ứng khi bên nhận đã sẵn sàng xử lý.

Tôi dự kiến kết hợp luôn việc cải tiến cơ chế lập lịch dịch vụ theo hướng đơn giản và mạnh mẽ hơn:

Hiện tại, các dịch vụ được chia thành hai loại: Dịch vụ nóng (có tin nhắn chờ xử lý) và dịch vụ lạnh (đang hoạt động nhưng hàng đợi trống). Các luồng xử lý chỉ cần lần lượt lấy dịch vụ từ hàng đợi nóng để thực thi hàm callback.

Thiết kế này giúp tiết kiệm tài nguyên vì số lượng dịch vụ nóng thường thấp hơn nhiều so với tổng số dịch vụ, giảm chi phí kiểm tra lặp lại liên tục trên toàn bộ hệ thống.

Liệu có thể áp dụng một thuật toán khác hiệu quả hơn?

Mỗi luồng xử lý sẽ thực hiện vòng lặp công việc như sau:

Bước 1: Duyệt qua toàn bộ dịch vụ, chọn ra các dịch vụ đang có công việc cần xử lý (bao gồm tin nhắn chờ xử lý hoặc tin nhắn bị nghẽn khi gửi đi). Loại bỏ những dịch vụ đang được xử lý bởi luồng khác (dịch vụ bận), sau đó đưa các dịch vụ còn lại vào một tập hợp cục bộ của luồng hiện tại.

Bước 2: Lần lượt xử lý các dịch vụ trong tập hợp cục bộ. Trong quá trình xử lý, nếu phát hiện dịch vụ đang được luồng khác xử lý, hàng đợi trống, hoặc hàng đợi gửi đi vẫn chưa khả dụng (bên nhận nghẽn) thì lập tức loại bỏ dịch vụ đó khỏi tập hợp.

Do điều kiện loại bỏ tương đối dễ dàng, trong khi không có cơ chế thêm mới, kích thước tập hợp sẽ ngày càng nhỏ sau mỗi vòng lặp. Để tránh trường hợp một vài dịch vụ chiếm dụng quá lâu, chúng ta có thể đặt giới hạn số vòng lặp - tạo khoảng “ngừng nghỉ” cho các dịch vụ quá nóng.

Khi tập hợp cục bộ trống hoặc đạt đến giới hạn vòng lặp, luồng xử lý sẽ kết thúc chu kỳ công việc và quay lại Bước 1.

Đây là những ý tưởng ban đầu, dự kiến sẽ bắt đầu triển khai sau kỳ nghỉ Tết Nguyên đán.

0%