Protobuf — nó là gì, định dạng dữ liệu nhị phân và nguyên lý hoạt động

Tác giả: IT Sectr Đã đăng: 2026-03-08 Thời gian đọc: 8 phút

Protocol Buffers (Protobuf) là một định dạng tuần tự hóa nhị phân cho dữ liệu có cấu trúc, được Google phát triển để trao đổi thông tin hiệu quả giữa các dịch vụ. Định dạng yêu cầu định nghĩa lược đồ trước trong các tệp .proto, từ đó mã được tạo cho nhiều ngôn ngữ. Theo tài liệu chính thức của Google, Protobuf cung cấp kích thước thông điệp nhỏ hơn JSON 3-10 lần. Protobuf được sử dụng trong gRPC, Google Maps và hàng ngàn dịch vụ nội bộ.

Các điểm chính

  • Protobuf — định dạng nhị phân của Google để tuần tự hóa với biểu diễn dữ liệu nhỏ gọn
  • Lược đồ .proto — mô tả cấu trúc dữ liệu được phân loại nghiêm ngặt với biên dịch thành mã
  • Hiệu suất — tuần tự hóa nhanh hơn và nhỏ gọn hơn JSON 3-10 lần
  • Tương thích ngược — phát triển lược đồ mà không phá vỡ các client hiện tại
  • gRPC — công nghệ truyền tải chính cho Protobuf trong microservice

Protocol Buffers là gì?

Protocol Buffers là một cơ chế tuần tự hóa dữ liệu có cấu trúc do Google phát triển, tương tự như JSON và XML, nhưng có một sự khác biệt cơ bản: dữ liệu được mã hóa ở định dạng nhị phân. Điều này có nghĩa là thông điệp Protobuf không thể đọc bằng mắt thường, nhưng nó chiếm ít không gian hơn đáng kể và xử lý nhanh hơn các đối tác dựa trên văn bản.

Protobuf được tạo ra nội bộ tại Google để giải quyết các vấn đề hiệu suất trong trao đổi dữ liệu giữa các dịch vụ. Năm 2008, công nghệ này trở thành một dự án mã nguồn mở với hỗ trợ cho nhiều ngôn ngữ: C++, Java, Python, Go, JavaScript, Kotlin, Swift, Dart và các ngôn ngữ khác. Phiên bản proto3, phát hành năm 2016, đã đơn giản hóa cú pháp và thêm hỗ trợ cho nhiều ngôn ngữ hơn.

Sự khác biệt chính giữa Protobuf và JSON là sự cần thiết phải xác định lược đồ trước khi bắt đầu trao đổi dữ liệu. Tệp lược đồ (.proto) mô tả cấu trúc thông điệp: các trường, kiểu và số thứ tự trường duy nhất. Từ lược đồ này, trình biên dịch protoc tạo ra các lớp trong ngôn ngữ đích để tuần tự hóa và giải tuần tự hóa.

Hệ sinh thái Protobuf

  • gRPC — framework RPC hiệu suất cao sử dụng Protobuf theo mặc định
  • protoc — trình biên dịch lược đồ .proto thành mã trong nhiều ngôn ngữ lập trình
  • Google API — hầu hết các API công khai của Google đều sử dụng Protobuf
  • Envoy Proxy — sử dụng Protobuf cho cấu hình và trao đổi dữ liệu

Kiến trúc Protobuf

Kiến trúc Protobuf bao gồm ba thành phần chính: ngôn ngữ định nghĩa lược đồ (.proto), trình biên dịch protoc và thư viện thời gian chạy cho các ngôn ngữ cụ thể. Nhà phát triển mô tả cấu trúc dữ liệu trong tệp .proto, chạy biên dịch và nhận được các lớp có sẵn để làm việc với dữ liệu này.

Mỗi trường trong thông điệp Protobuf có một số duy nhất (số trường) — đây không phải là số thứ tự mà là một thẻ được sử dụng trong định dạng nhị phân để nhận dạng trường. Các số từ 1 đến 15 được mã hóa trong một byte, các số từ 16 đến 2047 trong hai byte. Do đó, các trường quan trọng có tần suất sử dụng cao nên được đánh số từ 1 đến 15 để tối ưu kích thước.

Các kiểu dữ liệu Protobuf

Protobuf hỗ trợ nhiều loại kiểu: vô hướng (int32, int64, float, double, bool, string, bytes), liệt kê (enum), tổng hợp (message) và các kiểu đặc biệt (oneof, map). Mỗi kiểu có một biểu diễn nhị phân cụ thể được tối ưu hóa cho trường hợp sử dụng tương ứng.

Kiểu .proto Kiểu C++ Java/Kotlin Mô tả
double double double Số dấu phẩy động 64 bit
float float float Số dấu phẩy động 32 bit
int32 int32 int 32 bit, mã hóa độ dài thay đổi
int64 int64 long 64 bit, mã hóa độ dài thay đổi
string string String Chuỗi UTF-8
bytes string ByteString Byte tùy ý
bool bool boolean true / false

Lợi ích của định dạng nhị phân

Protobuf cung cấp ba lợi ích chính so với các định dạng dựa trên văn bản: kích thước thông điệp, tốc độ tuần tự hóa và phân loại chặt chẽ. Trong các hệ thống có tải cao và ứng dụng di động có lưu lượng giới hạn, những lợi ích này trở nên quan trọng.

Biểu diễn nhị phân của Protobuf sử dụng mã hóa độ dài thay đổi (Varint) cho các số: số nhỏ chiếm 1 byte, số lớn tối đa 10 byte. Điều này cho phép mã hóa hiệu quả các định danh, cờ và bộ đếm mà trong JSON hoặc XML sẽ chiếm hàng chục byte dưới dạng chuỗi văn bản. Ví dụ, số 150 trong Protobuf chiếm 2 byte, trong JSON — 3 byte (dưới dạng văn bản “150”), trong XML — 3 byte + thẻ.

Một lợi ích bổ sung là tương thích ngược. Thêm một trường mới vào lược đồ không làm hỏng các client cũ: chúng chỉ đơn giản bỏ qua các trường không xác định. Xóa một trường chỉ cần dành riêng số của nó để tránh xung đột trong tương lai.

Định nghĩa lược đồ .proto

Lược đồ .proto mô tả cấu trúc dữ liệu bằng một ngôn ngữ đặc biệt. Tệp bắt đầu bằng việc chỉ định cú pháp (proto3), gói và các phụ thuộc nhập. Mỗi thông điệp được định nghĩa với từ khóa message cùng các trường, trong đó mỗi trường có một kiểu, tên và số duy nhất.

Các thực hành tốt về lược đồ: số trường từ 1 đến 15 cho các trường thường xuyên sử dụng, tên có ý nghĩa, nhóm các trường liên quan vào một message riêng, sử dụng oneof cho các trường chỉ có thể là một trong nhiều tùy chọn.

protobuf
syntax = "proto3";

package mobileapp;

message User {
  string user_id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
  UserRole role = 5;
  repeated string tags = 6;
  map<string, string> metadata = 7;
}

enum UserRole {
  USER_ROLE_UNSPECIFIED = 0;
  USER_ROLE_USER = 1;
  USER_ROLE_ADMIN = 2;
  USER_ROLE_MODERATOR = 3;
}

Thông điệp lồng nhau

Các thông điệp có thể được lồng nhau: một trường profile kiểu Profile sẽ chứa dữ liệu người dùng bên trong. Hỗ trợ lồng nhau cho phép mô tả các cấu trúc phân cấp phức tạp mà không trùng lặp định nghĩa.

protobuf
syntax = "proto3";

package mobileapp;

message Order {
  string order_id = 1;
  repeated OrderItem items = 2;
  double total_price = 3;
  PaymentInfo payment = 4;
}

message OrderItem {
  string product_id = 1;
  string title = 2;
  int32 quantity = 3;
  double price = 4;
}

message PaymentInfo {
  string method = 1;
  string transaction_id = 2;
  double amount = 3;
}

Tạo mã từ .proto

Trình biên dịch protoc chuyển đổi các tệp .proto thành mã nguồn trong ngôn ngữ đích. Đối với Kotlin/Java, tham số --java_out được sử dụng, đối với Swift --swift_out (thông qua plugin Apple Swift Protobuf), đối với Dart --dart_out. Các lớp được tạo chứa các phương thức builder để xây dựng thông điệp và các phương thức tuần tự hóa/giải tuần tự hóa.

Trên Android, để làm việc với Protobuf, sử dụng plugin com.google.protobuf phiên bản 0.9+ trong Gradle. Sau khi thêm plugin và chỉ định các tệp .proto, quá trình xây dựng sẽ tự động tạo ra các lớp Kotlin sẵn sàng để sử dụng trong ứng dụng.

kotlin
import com.google.protobuf.kotlin.toByteString
import com.example.mobileapp.UserOuterClass.User

fun createUser(): User {
    return User.newBuilder()
        .setUserId("usr_001")
        .setName("IT Sectr")
        .setEmail("team@itsectr.com")
        .setAge(5)
        .setRole(UserOuterClass.UserRole.USER_ROLE_ADMIN)
        .addTags("mobile")
        .addTags("backend")
        .build()
}

fun serializeAndDeserialize(user: User): User {
    // Tuần tự hóa sang định dạng nhị phân
    val bytes = user.toByteArray()

    // Giải tuần tự hóa từ định dạng nhị phân
    return User.parseFrom(bytes)
}

Ví dụ sử dụng Protobuf

Protobuf đặc biệt hiệu quả trong kiến trúc microservice và ứng dụng di động. Hãy xem xét một kịch bản điển hình: một ứng dụng di động nhận danh sách sản phẩm từ máy chủ qua gRPC. Thông điệp Protobuf chứa thông tin sản phẩm bao gồm mã định danh, tên, giá và danh mục. Định dạng nhị phân giảm kích thước phản hồi từ 5 đến 8 lần so với JSON.

Dịch vụ gRPC trong Kotlin

Ví dụ về dịch vụ gRPC với Protobuf ở phía máy chủ. Dịch vụ khai báo phương thức RPC GetProducts, chấp nhận yêu cầu với các tham số phân trang và trả về danh sách sản phẩm. Việc triển khai Kotlin sử dụng các lớp được tạo để làm việc với các yêu cầu và phản hồi.

kotlin
import com.example.mobileapp.ProductServiceGrpcKt
import com.example.mobileapp.ProductOuterClass.Product
import com.example.mobileapp.ProductOuterClass.GetProductsRequest
import com.example.mobileapp.ProductOuterClass.GetProductsResponse

class ProductService : ProductServiceGrpcKt.ProductServiceCoroutineImplBase() {

    override suspend fun getProducts(
        request: GetProductsRequest
    ): GetProductsResponse {
        val products = fetchProductsFromDb(
            page = request.page,
            limit = request.limit
        )

        return GetProductsResponse.newBuilder()
            .addAllProducts(products)
            .setTotalCount(products.size)
            .build()
    }
}

Thiết lập Protobuf trong dự án Android

Thiết lập Protobuf trong dự án Android được thực hiện thông qua plugin Gradle com.google.protobuf. Plugin tự động chạy trình biên dịch protoc trong quá trình xây dựng và tạo các lớp Kotlin từ các tệp .proto. Để hoạt động, bạn cần thêm plugin vào build.gradle cấp dự án và áp dụng nó trong mô-đun ứng dụng.

Sau khi cấu hình Gradle, các tệp .proto được đặt trong thư mục src/main/proto. Trình biên dịch protoc xử lý chúng trong mỗi lần xây dựng, tạo ra các lớp Kotlin có thể được sử dụng trong mã ứng dụng. Điều quan trọng là phải chỉ định chính xác phiên bản protobuf và protoc để tránh xung đột phụ thuộc với các thư viện khác của dự án.

Đối với các dự án iOS, Protobuf được kết nối qua CocoaPods hoặc Swift Package Manager. Plugin Swift Protobuf tự động tạo các cấu trúc Swift tuân thủ giao thức Codable. Trong các dự án Dart cho Flutter, gói protobuf được sử dụng và việc biên dịch được thực hiện qua dart run protoc_plugin.

Câu hỏi thường gặp

Sự khác biệt giữa Protobuf và JSON là gì?

Protobuf là định dạng nhị phân với lược đồ bắt buộc, nhỏ gọn hơn JSON 3-10 lần. JSON dựa trên văn bản, có thể đọc được bởi con người và không yêu cầu lược đồ. Protobuf tuần tự hóa và giải tuần tự hóa nhanh hơn nhưng yêu cầu biên dịch tệp .proto. JSON dễ gỡ lỗi hơn và không yêu cầu thiết lập trước.

Làm thế nào để cài đặt trình biên dịch protoc?

Tải protoc từ bản phát hành GitHub của protobuf cho nền tảng của bạn. Đối với macOS, cài đặt qua brew install protobuf. Đối với Windows, tải xuống tệp zip và thêm protoc.exe vào PATH. Đối với Android/Kotlin, sử dụng plugin Gradle com.google.protobuf, plugin này tự động chạy biên dịch trong quá trình xây dựng.

gRPC là gì và nó liên quan đến Protobuf như thế nào?

gRPC là một framework RPC hiệu suất cao của Google sử dụng Protobuf làm ngôn ngữ định nghĩa giao diện (IDL) và định dạng tuần tự hóa. gRPC định nghĩa các dịch vụ và phương thức RPC trong các tệp .proto, tạo mã client và server, đồng thời hỗ trợ phát trực tiếp và các giao thức nhị phân.

Protobuf hỗ trợ những ngôn ngữ nào?

Được Google hỗ trợ chính thức: C++, Java, Kotlin, Python, Go, Ruby, C#, PHP, JavaScript, Objective-C, Swift và Dart. Cộng đồng đã phát triển hỗ trợ cho Rust, TypeScript, Scala, Lua và các ngôn ngữ khác. Đối với phát triển di động, có sẵn Kotlin/Java (Android) và Swift/Objective-C (iOS).

Làm thế nào để đảm bảo tương thích ngược của lược đồ?

Protobuf hỗ trợ tương thích ngược thông qua các quy tắc sau: không thay đổi số trường, không xóa trường (sử dụng reserved), thêm trường mới với số mới. Các client cũ bỏ qua các trường không xác định, các client mới nhận được giá trị mặc định cho các trường cũ bị thiếu.

Tổng kết

  • Protocol Buffers — định dạng tuần tự hóa nhị phân của Google với lược đồ dữ liệu bắt buộc
  • Kích thước thông điệp nhỏ hơn JSON 3-10 lần nhờ mã hóa Varint
  • Tốc độ tuần tự hóa và giải tuần tự hóa cao hơn định dạng văn bản
  • Lược đồ .proto cung cấp phân loại nghiêm ngặt và tạo mã cho 12+ ngôn ngữ
  • Tương thích ngược cho phép phát triển API mà không phá vỡ client
  • gRPC sử dụng Protobuf làm định dạng tiêu chuẩn cho giao tiếp microservice
  • Lý tưởng cho các hệ thống tải cao và ứng dụng di động có lưu lượng hạn chế

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm