Thêm Cơ Chế Hết Giờ Vào Skynet.call - nói dối e blog

Thêm Cơ Chế Hết Giờ Vào Skynet.call

Liên tục có người hỏi về cách xử lý vấn đề timeout khi thực hiện gọi dịch vụ liên hợp trong hệ thống skynet. Thực ra tôi không muốn mất thời gian giải thích tại sao việc tích hợp cơ chế timeout trực tiếp vào một hệ thống như skynet sẽ tạo ra sự phức tạp không cần thiết cho các ứng dụng xây dựng trên nền tảng này. Đây chỉ là giải pháp tình thế dành cho những người chưa nghĩ ra cách tốt hơn.

Hãy nhìn skynet như một hệ thống toàn cục, giống như việc bạn viết chương trình thông thường - hầu như không cần thiết đặt timeout cho các cuộc gọi API thông thường. Chỉ trong những trường hợp đặc biệt cần thiết, mới nên xây dựng một lớp bao bọc phía trên với tham số timeout (thay vì sử dụng trực tiếp skynet.call).

Khi nhận được yêu cầu này lần nữa vào hôm qua, tôi đã dành thời gian suy nghĩ kỹ lưỡng và tìm ra một giải pháp thú vị. Giải pháp này hoàn toàn không xâm phạm cấu trúc hiện tại, có thể tích hợp tính năng trả về khi hết giờ vào khung làm việc hiện tại một cách trơn tru. Dưới đây xin ghi lại phương pháp đã tìm ra:

Việc sửa đổi trực tiếp thư viện skynet.lua - thành phần lõi nền tảng - là việc không hiệu quả bởi sẽ phải chỉnh sửa hàng loạt mã điều phối hệ thống. Việc tạo ra một phiên bản call mới với tính năng timeout cũng sẽ dẫn đến tình trạng lặp code nghiêm trọng. Thật ra, chỉ cần xây dựng một dịch vụ tunnel trung gian chuyên chuyển tiếp yêu cầu, việc xử lý timeout sẽ trở nên đơn giản hơn nhiều.

Chúng ta có thể thiết kế một dịch vụ đại lý chuyên nghiệp với nhiệm vụ duy nhất là chuyển tiếp yêu cầu từ bên ngoài đến dịch vụ chỉ định, rồi gởi lại phản hồi. Trong dịch vụ này, ta thiết lập một ngưỡng timeout. Khi dịch vụ được chuyển tiếp không phản hồi trong thời gian quy định, hệ thống sẽ gởi tín hiệu lỗi về cho người gọi. Còn khi phản hồi thực sự đến sau đó sẽ bị bỏ qua một cách thông minh.

Không chỉ dừng lại ở việc xử lý timeout, dịch vụ đại lý này còn là nơi lý tưởng để triển khai các tính năng giám sát bổ sung như: thống kê lưu lượng, theo dõi hiệu năng, phát hiện dịch vụ gặp sự cố. Khi được đóng gói như một lớp bảo vệ bao bọc dịch vụ gốc, tính năng timeout hoàn toàn trong suốt với người dùng. Thời gian timeout có thể được thiết lập khi khởi động (hoặc thông qua file cấu hình) mà không cần thay đổi bất kỳ dòng mã nào trong hệ thống hiện có.

Nhiều người lo ngại việc thêm tầng đại lý sẽ ảnh hưởng đến hiệu năng, nhưng thực tế không phải vậy. Dịch vụ này gần như không giữ trạng thái nên có thể viết bằng C để tối ưu hóa hiệu suất. Công việc chính chỉ là chuyển tiếp tin nhắn, thậm chí không cần sao chép dữ liệu mà chỉ cần truyền con trỏ. Việc viết bằng C không chỉ giúp tiết kiệm bộ nhớ (không cần VM Lua) mà còn đảm bảo độ trễ chuyển tiếp chỉ ở mức O(1).

Về mặt tính thời gian, nếu không yêu cầu độ chính xác cao, việc tính theo đơn vị giây là hoàn toàn đủ. Thay vì yêu cầu timer hệ thống cho từng yêu cầu, ta có thể thiết kế cấu trúc vòng thời gian (time wheel). Sử dụng nhịp tim (heartbeat) với tần số thấp khoảng 10Hz (hoặc 2Hz trong trường hợp không cần phản hồi nhanh) để kiểm tra các yêu cầu đã vượt quá thời gian chờ chưa. Nếu có, lập tức gởi tín hiệu lỗi.

Hiện tại tôi chưa có ý định tích hợp tính năng này vào phiên bản chính thức của skynet đang phát triển. Tuy nhiên nếu tính năng này được chứng minh hữu ích qua các dự án thực tế, chúng tôi sẽ xem xét bổ sung trong các phiên bản tương lai.

Tạm thời, tôi đã thực hiện một bản demo đơn giản bằng Lua và đăng tải trên Gist, các bạn quan tâm có thể

Trong repo này có hai file: totun.lua - bản triển khai dịch vụ đại lý, và testtun.lua - chương trình kiểm thử. Đặc biệt, tôi sử dụng trực tiếp các API lõi của skynet mà không thông qua các lớp bao bọc cao cấp, giúp dễ dàng chuyển đổi sang phiên bản C khi cần thiết. Toàn bộ quá trình xử lý không sử dụng coroutine, chỉ tương tác thuần túy với các hàm C.

Đây cũng là tài liệu tham khảo quý giá cho những ai muốn tìm hiểu nguyên lý hoạt động của hệ thống skynet. Qua đó, bạn sẽ hiểu rõ cách một dịch vụ skynet vận hành chỉ với việc thiết lập hàm callback xử lý thông điệp.

Do không sử dụng các thành phần khung cao cấp, dù là dịch vụ viết bằng Lua nhưng không thể khởi động qua skynet.newservice, cũng không thể quan sát trạng thái qua debug console hay điều khiển bằng các lệnh gỡ lỗi. Nếu muốn đưa vào sử dụng thực tế, cần thiết kế thêm lớp bao bọc phù hợp.

Đặc biệt, dịch vụ đại lý này không giới hạn ở mô hình 1:1 mà có thể hỗ trợ mô hình nhiều-đối-một (many-to-one). Tức là nhiều người gọi có thể chia sẻ cùng một dịch vụ đại lý. Cơ chế này được thực hiện thông qua việc đánh lại số hiệu phiên làm việc (session). Điều này cho phép bạn quản lý tập trung các dịch vụ cần được bảo hộ theo mô hình service mgr, đảm bảo mỗi dịch vụ chỉ có duy nhất một đại lý, thay vì tạo mới ở từng nơi sử dụng riêng lẻ.

0%