REST service đã trở nên quen thuộc như một phương pháp trao đổi dữ liệu giữa Client và Server. Ưu điểm của REST là sử dụng định dạng văn bản (JSON, XML) đơn giản, dễ học và gỡ lỗi. Tuy nhiên, trong bài viết này, chúng ta sẽ khám phá một lựa chọn khác, không mới nhưng vẫn rất hữu ích: Protocol Buffers (Protobuf).
Protocol Buffers, hay Protobuf, là một cơ chế trung lập về ngôn ngữ và nền tảng (language-neutral, platform-neutral) được Google phát triển. Phiên bản nội bộ của nó ra đời từ năm 2001, và phiên bản công khai đầu tiên được giới thiệu vào năm 2008. Về cơ bản, Protobuf được sử dụng để tuần tự hóa (serialize) các đối tượng, tương tự như XML hoặc JSON. Nó lưu trữ dữ liệu có cấu trúc, có thể được tuần tự hóa và giải tuần tự hóa (deserialize) tự động bởi nhiều ngôn ngữ khác nhau. Protobuf được thiết kế để trở nên trung lập về ngôn ngữ/nền tảng và có khả năng mở rộng. Hiện tại, Protobuf hỗ trợ các ngôn ngữ như C++, C, Go, Java và Python.
Protobuf là một mã nguồn mở dùng để mã hóa dữ liệu có cấu trúc, được phát triển tại Google. Nó đặc biệt hữu ích trong việc xây dựng các chương trình giao tiếp với nhau thông qua mạng hoặc để lưu trữ dữ liệu. Bạn chỉ cần định nghĩa một “message” cho từng cấu trúc dữ liệu bạn muốn tuần tự hóa (tương tự như một lớp trong Java) bằng cách sử dụng file đặc tả .proto.
Từ file .proto, trình biên dịch Protobuf (protoc) sẽ tạo ra code thực hiện việc mã hóa và phân tích cú pháp dữ liệu Protobuf sang định dạng binary hiệu quả. Tùy thuộc vào ngôn ngữ bạn chọn, nó sẽ tạo ra mã tương ứng.
Tiếp theo, chúng ta sẽ so sánh ưu và nhược điểm của Protocol Buffers với một số định dạng khác.
Mục Lục
So Sánh Protobuf, JSON và XML
Dưới đây là bảng so sánh tổng quan về Protobuf, JSON và XML:
| Tính năng | Protobuf | JSON | XML |
|---|---|---|---|
| Dữ liệu | Rất dày đặc, đầu ra nhỏ | Dễ đọc và chỉnh sửa bởi con người | Dễ đọc và chỉnh sửa bởi con người |
| Giải mã | Cần schema, định dạng binary không rõ ràng | Có thể phân tích cú pháp không cần schema | Có thể phân tích cú pháp không cần schema |
| Tốc độ | Rất nhanh, nhỏ hơn 3-10 lần so với XML/JSON | Chậm hơn Protobuf | Chậm hơn Protobuf |
| Khả năng đọc | Không dành cho con người (binary) | Dễ đọc | Dễ đọc |
| Code truy cập | Tạo code truy cập dữ liệu dễ sử dụng | Cần thư viện hỗ trợ | Cần thư viện hỗ trợ |
| Hỗ trợ trình duyệt | Hạn chế | Rất tốt | Tốt |
| Tiêu chuẩn | Không phải tiêu chuẩn chính thức | Tiêu chuẩn web | Tiêu chuẩn cho SOAP… |
| Công cụ hỗ trợ | protoc |
Nhiều thư viện và công cụ | xsd, xslt, sax, dom… |
Khi Nào Không Nên Sử Dụng Protobuf?
Mặc dù Protobuf có nhiều ưu điểm, nhưng có những tình huống mà bạn không nên sử dụng nó:
- Khi bạn cần dữ liệu dễ đọc: Nếu con người cần đọc và chỉnh sửa dữ liệu một cách trực tiếp, JSON hoặc XML sẽ là lựa chọn tốt hơn.
- Dữ liệu được sử dụng trực tiếp bởi trình duyệt: Protobuf không được hỗ trợ trực tiếp bởi trình duyệt web, do đó JSON là lựa chọn phù hợp hơn.
- Server viết bằng Javascript: Mặc dù có thư viện hỗ trợ Protobuf cho Javascript, nhưng việc sử dụng JSON có thể đơn giản và hiệu quả hơn.
- Gánh nặng vận hành lớn: Việc triển khai và vận hành một dịch vụ mạng sử dụng Protobuf có thể phức tạp hơn so với JSON hoặc XML.
Cách Sử Dụng Protobuf
Mỗi file .proto bắt đầu bằng một khai báo package, giúp ngăn chặn xung đột tên giữa các project khác nhau. Về cơ bản, bạn sẽ xác định cấu trúc dữ liệu của mình bằng cách sử dụng định dạng “message” trong file .proto. File này sau đó được sử dụng bởi protoc để tạo ra các phương thức cho phép bạn tuần tự hóa và giải tuần tự hóa dữ liệu, theo ngôn ngữ bạn chỉ định (Java, Golang, Python, …).
Bạn có thể định nghĩa một loại message trong .proto như sau:
// Request message for creating a new customer
message CustomerRequest {
int32 id = 1; // Unique ID number for a Customer.
string name = 2;
string email = 3;
string phone= 4;
message Address {
string street = 1;
string city = 2;
string state = 3;
string zip = 4;
bool isShippingAddress = 5;
}
repeated Address addresses = 5;
}
message CustomerResponse {
int32 id = 1;
bool success = 2;
}
message CustomerFilter {
string keyword = 1;
}
Mỗi loại message có một hoặc nhiều “field” được đánh số duy nhất. Các loại message lồng nhau có tập hợp các trường được đánh số duy nhất của riêng chúng. Các loại giá trị có thể là Number, Boolean, String, Byte, Collection và Enumeration. Bạn cũng có thể lồng các loại message khác, cho phép bạn cấu trúc dữ liệu theo thứ bậc tương tự như JSON.
Chỉ Định Field Type
Các field có thể được chỉ định là optional, required, hoặc repeated. Bạn cần chỉ định Field type (enum, int32, float, string … ). Các Field type chỉ là gợi ý để bảo vệ về cách Serialize một giá trị Field và tạo định dạng được mã hóa Message của bạn. Định dạng được mã hóa trông giống như một biểu diễn phẳng và nén của đối tượng của bạn. Bạn sẽ viết đặc tả này theo cùng một cách chính xác cho dù bạn đang sử dụng protobuf trong Python, Java hay C ++.
Chỉ Định Tag
Mỗi field trong định nghĩa message có một “tag” được đánh số duy nhất. Các tag này được sử dụng để xác định các field của bạn ở định dạng message binary và không nên thay đổi khi loại message của bạn được sử dụng. Lưu ý rằng các tag có giá trị trong phạm vi từ 1 đến 15 mất một byte để mã hóa, bao gồm số nhận dạng và field type. Các thẻ trong phạm vi 16 đến 2047 mất hai byte. Vì vậy, bạn nên dành các thẻ từ 1 đến 15 cho các yếu tố thông báo xảy ra rất thường xuyên.
Số thẻ nhỏ nhất bạn có thể chỉ định là 1 và lớn nhất là 229 – 1 hoặc 536.870.911. Bạn cũng không thể sử dụng các số 19000 đến 19999 vì chúng được dành riêng cho việc triển khai Protobuf – compiler sẽ báo lỗi nếu bạn sử dụng một trong những số dành riêng này trong file .proto. Tương tự, bạn không thể sử dụng bất kỳ tag dành riêng trước đó.
Chỉ Định Quy Tắc Field
Bạn xác định các Message field là một trong những điều sau đây:
required: Đối với các required field, giá trị ban đầu phải được cung cấp, nếu không field không được khởi tạo. (Lưu ý: Hiện tại không khuyến khích sử dụngrequiredvì có thể gây ra các vấn đề về tương thích ngược.)optional: Đối với các optional field, nếu không khởi tạo, thì giá trị mặc định sẽ được gán cho field. Bạn có thể chỉ định giá trị mặc định.repeated: Field có thể được lặp lại bất kỳ số lần nào trong một message. Thứ tự của các giá trị lặp lại sẽ được bảo tồn.
Lưu ý: các trường lặp lại của các kiểu scalar numeric không được mã hóa hiệu quả nhất có thể. Mã mới nên sử dụng tùy chọn đặc biệt [packed = true] để có được mã hóa hiệu quả hơn, như dưới đây:
repeated int32 samples = 4 [packed=true];
Enumerations
Khi bạn xác định loại message, bạn có thể muốn một trong các field của nó chỉ có một trong danh sách giá trị được xác định trước. Sử dụng enumerations (enum) để làm điều này.
enum Foo {
FIRST_VALUE = 0;
SECOND_VALUE = 1;
}
Kết Luận
Protocol Buffer cung cấp một số lợi thế hấp dẫn so với JSON và XML để gửi dữ liệu qua mạng giữa các internal service. Mặc dù không phải là sự thay thế hoàn toàn cho JSON và XML, đặc biệt là các dịch vụ được sử dụng trực tiếp bởi trình duyệt web, Protocol Buffer mang lại những lợi ích rất thiết thực về tốc độ mã hóa và giải mã, kích thước của dữ liệu truyền tải và khả năng định nghĩa cấu trúc dữ liệu chặt chẽ. Việc lựa chọn giữa Protobuf, JSON và XML phụ thuộc vào yêu cầu cụ thể của từng dự án.
Tài liệu tham khảo:
