حول المحتوى:
شرح عملي لكيفية استخدام Protobuf لتسريع الاتصال بين الخدمات في الأنظمة الكبيرة مقارنة بـ JSON.
في الأنظمة الكبيرة التي تعتمد على خدمات متعددة (Microservices)، يصبح شكل تمثيل البيانات عاملًا أساسيًا في الأداء. أغلب المطورين يبدأون باستخدام JSON لأنه بسيط وقابل للقراءة، لكن مع زيادة عدد الطلبات وحجم البيانات، تظهر الحاجة إلى حل أسرع وأكثر كفاءة. هنا يأتي دور Protocol Buffers من جوجل.
في هذا Protocol Buffers Tutorial سنشرح بشكل عملي كيف تستخدم Protobuf لتسريع الاتصال بين الخدمات، وما الفرق بينه وبين JSON، وكيف يمكن دمجه في تصميم الأنظمة الكبيرة مع REST أو gRPC.
Protocol Buffers هو نظام ترميز بيانات ثنائي (Binary Serialization) تم تطويره بواسطة جوجل. فكر فيه كبديل لـ JSON أو XML لكن:
.protoالفكرة الأساسية: بدل إرسال نص مثل JSON، يتم إرسال بيانات ثنائية مضغوطة مبنية على تعريف رسالة مشترك بين العميل والخادم.
لفهم أهمية Protobuf، علينا فهم تأثير تمثيل البيانات على أداء الأنظمة، خصوصًا في بيئات System Design في الأنظمة الكبيرة:
النتيجة:
.proto؟ ملف .proto هو المكان الذي تعرّف فيه شكل الرسائل التي ستتبادلها الخدمات. يمكنك اعتباره مشابهًا لتعريف الـ DTO أو Models في لغتك، لكن بشكل مستقل عن اللغة.
مثال بسيط لتعريف مستخدم:
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
bool is_active = 4;
} ملاحظات مهمة في هذا المثال:
أرقام الحقول لا تُستخدم فقط للترتيب؛ بل هي جزء من البروتوكول نفسه. عند إرسال البيانات، لا يُرسل اسم الحقل نصًا، بل يُستخدم رقم الحقل لتحديده. لهذا:
Protobuf يدعم مجموعة كبيرة من الأنواع الأساسية:
int32, int64, uint32, uint64, sint32, sint64float, doublestringbytes لتخزين بيانات خام (مثل صور صغيرة، أو بيانات مشفرة).bool.ويمكنك تعريف:
repeated.مثال أكثر تقدمًا:
syntax = "proto3";
message Address {
string city = 1;
string street = 2;
string country = 3;
}
enum UserRole {
USER = 0;
ADMIN = 1;
SUPPORT = 2;
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
bool is_active = 4;
Address address = 5;
repeated string tags = 6;
UserRole role = 7;
} الاستخدام العملي لـ Protobuf يعتمد على وجود Compiler اسمه protoc يقوم بتحويل ملفات .proto إلى كود جاهز في لغتك (Classes, Structs, Types).
يمكن تثبيته من صفحة GitHub الرسمية لـ protobuf أو من مدير الحزم في نظام التشغيل (apt, brew, choco…). بعد التثبيت تأكد من أن الأمر:
protoc --version يعطيك رقم إصدار بدون مشاكل.
.protoلنفرض أن لدينا ملفًا باسم user.proto يحتوي على التعريف السابق لـ User.
مثال بلغة Python (لأنها منتشرة بين قرّاء المدونة ولدينا مقالات مثل البرمجة غير المتزامنة في بايثون):
protoc --python_out=. user.proto سينتج ملف Python (مثل user_pb2.py) يحتوي على كلاس User و Address و UserRole جاهزة للاستخدام.
بنفس الطريقة، للغات أخرى:
--java_out--csharp_out--go_outسنأخذ مثالًا بسيطًا بلغة Python يُظهر الفرق بين JSON و Protobuf في التمثيل والاستخدام.
user.protosyntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
bool is_active = 4;
} protoc --python_out=. user.proto import user_pb2
import json
# إنشاء كائن User باستخدام Protobuf
u = user_pb2.User()
u.id = 1
u.name = "Ahmed"
u.email = "[email protected]"
u.is_active = True
# تحويل إلى bytes لإرسالها عبر الشبكة
data_protobuf = u.SerializeToString()
# للمقارنة: تمثيل نفس البيانات بـ JSON
user_json = {
"id": 1,
"name": "Ahmed",
"email": "[email protected]",
"is_active": True
}
data_json = json.dumps(user_json).encode("utf-8")
print("Protobuf size:", len(data_protobuf))
print("JSON size:", len(data_json))
# في الطرف الآخر (الخادم) نفك التشفير:
u2 = user_pb2.User()
u2.ParseFromString(data_protobuf)
print(u2.name, u2.email) في هذا المثال، ستلاحظ غالبًا أن حجم Protobuf أصغر من JSON، وخاصة مع رسائل أكبر أو تحتوي على أعداد صحيحة كثيرة.
الاستخدام الأشهر لـ Protobuf اليوم هو مع gRPC، وهو إطار عمل من جوجل يعتمد على HTTP/2 وProtobuf لتوفير:
.proto نفسهامع gRPC لا تستخدم فقط الرسائل (message)، بل تعرّف أيضًا الخدمات (service) و الـ RPC methods داخل الملف.
مثال مبسط لتعريف خدمة في user.proto:
syntax = "proto3";
service UserService {
rpc GetUser (GetUserRequest) returns (User);
}
message GetUserRequest {
int32 id = 1;
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
bool is_active = 4;
} عند تشغيل protoc مع إضافات gRPC، سيتم توليد كود الخادم (Server Stub) والعميل (Client Stub)، مما يجعل استدعاء الخدمات يشبه استدعاء دوال عادية بدل التعامل مع HTTP يدويًا.
من منظور تصميم الأنظمة وأداء الخدمات، يمكن تلخيص الفرق كالتالي:
لتحصل على أقصى استفادة من Protobuf، من المهم اتباع بعض الممارسات:
repeated للقوائم بدل إرسال JSON string داخل حقل واحد. Protobuf ثنائي وغير قابل للقراءة المباشرة مثل JSON، لكن يمكنك استخدام أدوات لتفريغها (مثل protoc --decode) عندما تحتاج إلى Debugging.
في كثير من التصاميم، يتم استخدام JSON لواجهات REST العامة، وProtobuf للاتصال الداخلي بين الخدمات حيث الأداء أهم.
أما في التطبيقات الصغيرة أو APIs العامة المفتوحة للمطورين، قد يكون JSON خيارًا أبسط وأكثر توافقًا، خاصةً إذا لم يكن حمل الشبكة عائقًا كبيرًا في البداية.
في مشروعات موجودة مسبقًا تعتمد على JSON وREST، يمكنك اعتماد نهج تدريجي:
يمكنك ربط هذا مع مفاهيم تصميم الأنظمة التي تطرقنا لها سابقًا في مقال مقدمة في System Design للمطورين، خاصّة عندما تصمم واجهات بين الخدمات المختلفة.
Protocol Buffers ليس مجرد تقنية جديدة لتجربتها؛ هو أداة مصممة خصيصًا لحل مشكلة واضحة: نقل بيانات أسرع وأصغر بين الخدمات. في المشاريع الصغيرة، لن تشعر بفارق كبير، لكن في الأنظمة الكبيرة ذات الـ Microservices الكثيفة، يمكن أن يحدث فرقًا واضحًا في:
إذا كنت تعمل على نظام ينمو باستمرار، أو تبني بنية Microservices جديدة، أو تفكر في حلول لتسريع الاتصال بين الخدمات، فإن فهم واستخدام Protobuf عمليًا خطوة منطقية جدًا، وستساعدك على تصميم واجهات أكثر كفاءة وقابلة للتوسع.
يمكنك البدء بتجربة بسيطة، تحويل جزء صغير من الاتصال بين خدمتين من JSON إلى Protobuf، وقياس الأداء، ثم تعميم التجربة على باقي أجزاء النظام إن كانت النتائج مرضية.
شرح عملي لكيفية استخدام Protobuf لتسريع الاتصال بين الخدمات في الأنظمة الكبيرة مقارنة بـ JSON.
مساحة اعلانية