Cách Sử Dụng Sproto Rpc - nói dối e blog

Cách Sử Dụng Sproto Rpc

Sử dụng giao thức RPC của sproto

Tôi đã tự thiết kế giao thức sproto như một giải pháp thay thế cho Google Protocol Buffers vốn được sử dụng trong dự án mới của chúng tôi. Trong các dự án trước đây, tôi thường xuyên sử dụng protobuf, nhưng trong môi trường phát triển chủ yếu dựa trên ngôn ngữ Lua, sproto lại là lựa chọn phù hợp hơn nhiều. Điều này xuất phát từ thực tế Google không cung cấp thư viện hỗ trợ chính thức cho Lua với protobuf.

Trên thị trường hiện nay, có hai giải pháp phổ biến nhất để triển khai protobuf trên nền tảng Lua là pbc (protobuf binding cho Lua) và protoc-gen-lua. Tôi từng là người trực tiếp phát triển và duy trì pbc trong nhiều năm, đồng thời cũng từng sử dụng protoc-gen-lua trong một dự án trước đây do đồng nghiệp phát triển. Ngoài ra, tôi cũng từng xây dựng các thư viện phụ trợ cho protobuf như phiên bản ActionScript 3 và Erlang, được nhiều lập trình viên sử dụng. Trải nghiệm thực tế này đã giúp tôi có đủ cơ sở để đánh giá ưu nhược điểm của protobuf từ đó quyết định phát triển một giải pháp mới hoàn toàn dựa trên sproto.

Trong bài viết này, tôi sẽ không so sánh ưu nhược điểm giữa sproto và protobuf, mà tập trung vào cách sử dụng API RPC của sproto - một phần tài liệu chưa được mô tả đầy đủ nhưng nhiều người quan tâm.

Trong các mô hình RPC kiểu request/response, ngoài nội dung tin nhắn chính cần được truyền tải, còn có hai thông tin quan trọng khác: loại yêu cầu (request type) và phiên làm việc (session). Cần phân biệt rõ loại yêu cầu và loại tin nhắn, vì nhiều yêu cầu khác nhau có thể chia sẻ cùng một cấu trúc tin nhắn. Do đó, sproto yêu cầu mã hóa bổ sung để xác định loại yêu cầu. Người dùng không nhất thiết phải tạo kiểu tin nhắn mới cho mỗi loại yêu cầu, mà có thể định nghĩa trực tiếp cấu trúc tin nhắn request và response ngay trong giao thức RPC.

Trong thực tế, chúng ta thường sử dụng số nguyên để biểu thị loại tin nhắn. Dù có thể dùng chuỗi ký tự, nhưng với các giao thức có schema như sproto, việc sử dụng số nguyên sẽ hiệu quả hơn về mặt hiệu suất. Tương tự, session dù có thể dùng cả số nguyên hoặc chuỗi ký tự, nhưng việc sinh ra chuỗi số nguyên duy nhất dễ dàng hơn và hiệu quả hơn trong quá trình mã hóa.

Khi gửi một yêu cầu từ xa, chúng ta cần truyền đi ba thông tin cơ bản: loại yêu cầu, session ID do phía yêu cầu đảm bảo tính duy nhất, và nội dung dữ liệu yêu cầu. Khi nhận được yêu cầu, phía server sẽ giải mã dữ liệu dựa trên loại yêu cầu, phân phối cho bộ xử lý tương ứng và ghi nhận session ID. Sau khi xử lý xong, hệ thống sẽ đóng gói thông tin phản hồi kèm theo session ID để gửi trả về client.

Lưu ý quan trọng: Phần phản hồi (response) không cần truyền tải loại tin nhắn, vì session ID duy nhất đã xác định rõ đây là phản hồi cho yêu cầu nào. Client, người chịu trách nhiệm tạo session ID duy nhất, sẽ lưu trữ thông tin tương ứng giữa session ID và loại yêu cầu ban đầu, từ đó có thể giải mã chính xác nội dung phản hồi.

Trong trường hợp tin nhắn chỉ mang tính chất đẩy một chiều (như mô hình publish/subscribe), chúng ta hoàn toàn có thể bỏ qua phần session ID và không cần yêu cầu phản hồi.

Sproto cung cấp hai cách tiếp cận để triển khai RPC:

Kiểu 1: Cách tiếp cận trừu tượng hóa cao

Người dùng cần định nghĩa một kiểu tin nhắn gói (package) chứa hai trường: type và session. Mỗi gói tin sẽ bắt đầu bằng cấu trúc package này, theo sau là nội dung tin nhắn được mã hóa và nén bằng thuật toán 0-pack tích hợp sẵn của sproto.

Thông qua API sproto:host, chúng ta có thể tạo một bộ phân phối tin nhắn (host) để xử lý các thông điệp RPC. Bộ phân phối này có khả năng xử lý cả yêu cầu và phản hồi, cho phép mỗi đầu kết nối vừa đóng vai trò client vừa đóng vai trò server. Hàm host:dispatch sẽ xác định gói tin nhận được là yêu cầu hay phản hồi, đồng thời trích xuất nội dung tương ứng.

Khi host muốn gửi yêu cầu, hàm host:attach sẽ tạo ra một hàm đóng gói tin nhắn. Hàm này sẽ kết hợp type, session và content thành một chuỗi nhị phân có thể được phía nhận xử lý thông qua host:dispatch.

Kiểu 2: Cách tiếp cận linh hoạt hơn

Sproto cung cấp bộ API cơ bản như sau:

  • sproto:request_encode
  • sproto:request_decode
  • sproto:response_encode
  • sproto:response_decode

Nhóm API này không tự động xử lý type và session, mà yêu cầu người dùng tự quản lý. Khi biết chắc một tin nhắn là yêu cầu hay phản hồi, bạn có thể gọi các hàm tương ứng để mã hóa/giải mã dữ liệu.

Ví dụ, nếu server chỉ cần xử lý yêu cầu, bạn có thể sử dụng sproto:request_decode sau khi xác định được type (từ nguồn bên ngoài) và lưu trữ session. Khi cần phản hồi, chỉ cần gọi sproto:response_decode và đính kèm session đã lưu.

Thiết kế này đặc biệt phù hợp với mẫu message server của Skynet, nơi session đã được quản lý ở tầng dưới, khiến kiểu API trừu tượng hóa cao trở nên kém thích hợp hơn.

Sự khác biệt này phản ánh rõ triết lý thiết kế của sproto: cung cấp cả hai lựa chọn - vừa có API tiện lợi để sử dụng nhanh, vừa có API linh hoạt để tùy biến sâu, phù hợp với đa dạng nhu cầu triển khai thực tế.

0%