Giám Sát Dịch Vụ Và Gọi Từ Xa Trong Skynet - nói dối e blog

Giám Sát Dịch Vụ Và Gọi Từ Xa Trong Skynet

Trong các hệ khung dựa trên mẫu Actor, bài toán phức tạp nhất là xử lý hậu quả khi một actor gặp lỗi và thoát bất ngờ. Erlang đã giải quyết vấn đề này theo cách trực tiếp nhưng hiệu quả thông qua phương thức spawn_link - khi một process (tương đương Actor trong Erlang) kết thúc, toàn bộ process liên kết cũng sẽ tự động dừng theo. Tuy nhiên cách tiếp cận này có phần cứng nhắc khi áp dụng vào hệ sinh thái Skynet.

Ban đầu, tôi không muốn xây dựng cơ chế xử lý tự động ở tầng nền cho vấn đề này. Triết lý thiết kế Skynet giả định rằng mọi dịch vụ đều ổn định, và nếu có dịch vụ nào tiềm ẩn nguy cơ dừng giữa chừng, logic xử lý nên được triển khai ở tầng cao hơn. Đặc biệt, nhờ cơ chế coroutine của Lua, một service Lua thực tế có thể chứa nhiều actor độc lập - mỗi coroutine mới chính là một actor riêng biệt. Việc ràng buộc cứng tuổi thọ các service ở tầng hệ thống sẽ phá vỡ tính linh hoạt này.

Vấn đề trở nên nan giải khi gặp trường hợp service bị dừng giữa lúc đang thực hiện yêu cầu từ xa qua skynet.call. Giải pháp dùng timeout theo tôi là cách né tránh, không giải quyết tận gốc mà còn gây phát sinh độ phức tạp. Đây chính là lý do tôi đặc biệt thận trọng khi xử lý cơ chế giám sát vòng đời service.

Gần đây, tôi đã tích hợp thêm cơ chế monitor vào lõi Skynet. Khi một service dừng, thông báo sẽ được đẩy từ tầng hệ thống lên cho logic ứng dụng xử lý. Tuy nhiên, tôi cố ý không ràng buộc logic xử lý sự kiện này vào hệ thống, mà tạo ra phương thức skynet.monitor để đăng ký một service đặc biệt đảm nhiệm vai trò này.

Về mặt thiết kế, tôi chủ động không áp dụng cơ chế đặt tên dịch vụ cho monitor (trái ngược với việc đặt tên launcher trong phiên bản đầu). Đây là để đảm bảo các tính năng mức nền tảng không nên phụ thuộc vào tên dịch vụ - một khái niệm nên được quản lý ở tầng cao hơn. Việc hỗ trợ đặt tên dịch vụ hiện tại chỉ tồn tại vì lý do tương thích ngược, và các tính năng mới tôi phát triển sẽ không còn lệ thuộc vào nó nữa.

Đi kèm với đó là một monitor mẫu với logic đơn giản, trong khi phiên bản dùng thực tế trong dự án của chúng tôi phức tạp hơn nhiều.

Tuy nhiên, chỉ riêng monitor chưa đủ để hoàn thiện hệ thống. Trong bản cập nhật mới đây, tôi đã nâng cấp skynet.call với hai cải tiến quan trọng:

  1. API skynet.watch:

    • Giúp theo dõi chủ động các dịch vụ không ổn định
    • Hạn chế can thiệp vào skynet.call truyền thống do phần lớn dịch vụ đều ổn định
    • Khi service bị watch dừng, thông báo sẽ được gửi đến monitor để xử lý
  2. Cơ chế phục hồi coroutine:

    • skynet.call sẽ lưu trữ session/dịch vụ đang xử lý vào bảng tạm
    • Khi monitor báo dịch vụ biến mất, các coroutine liên quan sẽ được đánh thức và ném ra ngoại lệ
    • Điều này đảm bảo skynet.call không bị treo khi service đích dừng giữa lúc xử lý

Ban đầu thiếu vắng cơ chế lan truyền ngoại lệ - ví dụ khi một coroutine bị lỗi do gọi từ xa, lỗi đó nên được truyền ngược lại nơi gọi. Chúng tôi đã triển khai tính năng này trong dự án nội bộ, nhưng đang cân nhắc cách tích hợp mượt mà vào kho mã cốt lõi trên GitHub.

Cập nhật ngày 10/12: Tôi nhận ra cơ chế lan truyền ngoại lệ có thể tận dụng kênh truyền hiện có với vài dòng mã bổ sung. Bản vá mới đã tích hợp tính năng này, đảm bảo khi phương thức từ xa gặp lỗi, nơi gọi sẽ nhận được chính xác ngoại lệ đó thay vì bị treo.

0%