Cách Thức Dừng Hệ Thống Skynet Một Cách an Toàn
Thiết kế ban đầu của Skynet hoàn toàn chưa quan tâm đến vấn đề dừng hệ thống có kiểm soát. Về sau, để phục vụ mục đích gỡ lỗi rò rỉ bộ nhớ, hệ thống mới bổ sung lệnh ABORT có thể tiêu diệt toàn bộ dịch vụ đang hoạt động. Việc dừng an toàn một hệ thống phân tán luôn là một bài toán phức tạp. Phiên bản nội bộ của chúng tôi đã đầu tư rất nhiều công sức cho vấn đề này, tuy nhiên tôi cảm thấy cách tiếp cận còn chưa tinh gọn nên chưa sẵn sàng đưa lên phiên bản mã nguồn mở.
Hôm nay, khi Xiao Jing lại đề cập đến phương án dừng hệ thống, chúng tôi đã dành thời gian bàn luận kỹ hơn về chủ đề này. Quan điểm cá nhân của tôi là: quy trình dừng an toàn có mối liên hệ mật thiết với logic nghiệp vụ cụ thể. Nếu tích hợp hàng loạt cơ chế thông báo vào khung nền để mỗi dịch vụ tự điều phối dừng thì không chỉ làm tăng độ phức tạp của hệ thống mà còn không thực sự giải quyết triệt để. Phiên bản nội bộ hiện tại áp dụng mô hình thông báo dừng dịch vụ kèm mức độ ưu tiên, nhưng thiết kế này còn khá rườm rà và chưa hiệu quả như mong đợi.
Trước đây tôi từng nghĩ đến giải pháp mô phỏng cách làm của hệ điều hành: Khi cần tiêu diệt một dịch vụ, trước tiên gửi tín hiệu yêu cầu dừng, cho phép dịch vụ đó thời gian ngắn để hoàn tất các tác vụ đang xử lý. Nếu quá thời hạn mà dịch vụ vẫn chưa dừng, tiến hành giết cứng. Tuy nhiên phương pháp này tiềm ẩn rủi ro do không đảm bảo tính toàn vẹn dữ liệu, trong khi việc đợi dịch vụ tự dừng lại có thể dẫn đến tình trạng tắc nghẽn (deadlock). Với các hệ thống do chính mình thiết kế, các kỹ sư lẽ ra có thể hiểu rõ cách thức dừng an toàn. Trong thực tế, chỉ một số nhỏ dịch vụ cần thực hiện các quy trình dọn dẹp đặc biệt - ví dụ như tầng cơ sở dữ liệu phải hoàn tất việc ghi dữ liệu chưa lưu trước khi dừng; đa phần các dịch vụ trạng thái có thể dừng trực tiếp; và một số ít khác có quy trình dừng phức tạp hơn, như cổng kết nối mạng cần lần lượt: dừng cổng lắng nghe, đóng từng kết nối khách hàng, rồi mới dừng bản thân dịch vụ.
Thay vì yêu cầu mỗi dịch vụ tự xử lý quy trình dừng, tôi cho rằng nên thiết kế một dịch vụ chuyên trách điều phối toàn bộ quá trình dừng hệ thống. Dịch vụ launcher hiện tại đã kiểm soát phần lớn quy trình khởi động, có thể mở rộng để quản lý cả việc dừng hệ thống. Hoặc ta có thể tách riêng chức năng này thành một thành phần độc lập. Dù lựa chọn nào, cũng không nên tích hợp quá nhiều logic vào tầng nền tảng. Các dịch vụ có quy trình dừng phức tạp cần đăng ký với bộ quản lý dừng, để bộ này nắm rõ toàn bộ dịch vụ đang vận hành và đưa ra thứ tự ưu tiên dừng dựa trên kiến trúc hệ thống tổng thể.
Khung nền có thể hỗ trợ một số thao tác nhỏ để đơn giản hóa quy trình này. Ví dụ: thiết kế khả năng một dịch vụ có thể theo dõi sự kiện dừng của các dịch vụ khác, thay vì phải báo cáo trạng thái dừng tự động. Tôi deliberately không thiết kế cơ chế dừng theo kiểu “process link” như Erlang vì mô hình process link n:m sẽ khiến việc triển khai mô-đun quản lý kết nối trở nên quá phức tạp. Giải pháp thay thế là tập trung xử lý toàn bộ sự kiện dừng tại một điểm duy nhất, tại đây có thể triển khai logic kiểu process link nếu cần.
Cơ chế hiện tại được cài đặt qua phương thức skynet.monitor trong skynet.lua. Khi khởi động hệ thống, ta sẽ gọi hàm này để kích hoạt một dịch vụ giám sát. Tôi đã xây dựng một dịch vụ giám sát đơn giản tên simplemonitor.lua, ghi log mỗi khi có dịch vụ dừng. Người dùng có thể mở rộng chức năng của dịch vụ này để thực hiện các tác vụ nâng cao hơn.