0

ĐÓNG GÓI DỮ LIỆU TRONG gRPC: NGUYÊN LÝ "MỘT VÀO, MỘT RA"

trong lập trình thông thường (như viết hàm bằng PHP, Go hay JavaScript), chúng ta đã quen với việc truyền bao nhiêu tham số tùy thích vào một hàm: createUser($name, $email, $phone, $password).

Tuy nhiên, khi bước vào thế giới gRPC và Protocol Buffers, luật chơi thay đổi hoàn toàn vì lý do tối ưu hóa hiệu năng mạng và khả năng tương thích ngược.

Hãy cùng mổ xẻ nguyên lý cốt lõi này: Đóng gói dữ liệu (Encapsulation) trong gRPC.

1. Luật Chơi Cứng Nhắc Của gRPC: Chỉ 1 Tham Số Vào và 1 Tham Số Ra

Khi bạn định nghĩa một hàm rpc trong file .proto, trình biên dịch Protobuf áp dụng một quy tắc cực kỳ nghiêm ngặt:

  • Mỗi hàm rpc chỉ được phép nhận đúng MỘT tham số đầu vào (Request).

  • Và chỉ được phép trả về đúng MỘT tham số đầu ra (Response).

Bạn hoàn toàn không thể viết một hàm gRPC nhận rải rác nhiều tham số kiểu như:

Protocol Buffers

// ❌ SAI HOÀN TOÀN: gRPC không cho phép truyền nhiều tham số riêng lẻ
rpc CreateUser (string name, string email, string phone) returns (string status);

Nếu cố tình làm vậy, trình biên dịch sẽ văng lỗi ngay lập tức.

2. Giải Pháp: Nghệ Thuật Đóng Gói Với Từ Khóa message

Để vượt qua giới hạn "mỗi hàm chỉ nhận một tham số", gRPC buộc chúng ta phải sử dụng kỹ thuật Đóng gói dữ liệu (Encapsulation) thông qua từ khóa message.

Thay vì truyền từng biến lẻ tẻ, bạn gom toàn bộ các thông tin cần thiết vào chung một cấu trúc đóng gói (giống như việc cho tất cả nguyên liệu vào một chiếc hộp quà trước khi gửi đi):

Protocol Buffers

syntax = "proto3";

package user;

service UserService {
    // 📦 Hàm RPC chỉ nhận ĐÚNG 1 "hộp quà" CreateUserRequest 
    // và trả về ĐÚNG 1 "hộp kết quả" CreateUserResponse
    rpc CreateUserBO (CreateUserRequest) returns (CreateUserResponse);
}

// "Hộp quà" đóng gói toàn bộ thông tin cần thiết để tạo user
message CreateUserRequest {
    string name     = 1;
    string email    = 2;
    string phone    = 3;
    string password = 4;
    string username = 5;
    string address  = 6;
}

// "Hộp kết quả" trả về sau khi tạo thành công
message CreateUserResponse {
    int64 user_id   = 1;
    string email    = 2;
    string status   = 3;
}

3. Tại Sao gRPC Lại Ép Buộc Quy Tắc Đóng Gói Này?

Việc thiết kế mỗi rpc chỉ nhận đúng một message duy nhất mang lại những lợi ích kiến trúc cực kỳ to lớn trong các hệ thống Microservices:

  1. Khả năng tương thích ngược siêu việt (Backward Compatibility): Đây là điểm ăn tiền lớn nhất của Protobuf. Nhờ việc gom dữ liệu vào message và sử dụng cơ chế định danh số thứ tự trường (field numbers như = 1, = 2), sau này nếu hệ thống phát triển và bạn cần bổ sung thêm trường avatar_url = 7 vào CreateUserRequest, các client cũ chưa cập nhật code vẫn gọi hàm này bình thường mà không làm sập hệ thống. Nếu bạn dùng danh sách tham số rời rạc, việc thêm bớt một tham số sẽ làm thay đổi toàn bộ chữ ký hàm (signature) và phá vỡ cấu trúc gọi mạng.

  2. Đóng gói dữ liệu nhị phân hiệu quả (Binary Serialization): Khi dữ liệu được gói gọn trong một struct message duy nhất, thuật toán nén nhị phân của Protocol Buffers sẽ dễ dàng mã hóa chúng thành một chuỗi byte nhỏ gọn siêu tốc để truyền qua HTTP/2, giảm thiểu tối đa băng thông mạng so với việc truyền các chuỗi JSON cồng kềnh.

  3. Mã nguồn sinh ra gọn gàng (Clean Code Generation): Khi biên dịch file .proto, trình biên dịch sẽ tạo ra các cấu trúc Struct cực kỳ sạch sẽ trong Go (ví dụ: type CreateUserRequest struct { Name string, Email string... }). Lập trình viên chỉ cần truyền một con trỏ &req duy nhất vào hàm gọi gRPC, giúp code ở tầng Handler cực kỳ gọn gàng và dễ bảo trì.

💡 Lời Kết

Nguyên lý đóng gói dữ liệu (message) trong gRPC không chỉ là một quy tắc cú pháp bắt buộc, mà nó còn là tư duy thiết kế giao diện hướng cấu trúc. Việc gom nhóm các tham số vào message giúp "hợp đồng giao tiếp" giữa các microservices trở nên chặt chẽ, dễ mở rộng và bền vững theo năm tháng!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí