خدماتنا كانت تتحدث بلغة JSON بطيئة: كيف أنقذنا gRPC من جحيم الاتصال غير الفعال بين الخدمات المصغرة؟

يا أهلاً وسهلاً فيكم يا جماعة. معكم أخوكم أبو عمر، من قلب فلسطين الحبيبة، جاي أحكيلكم قصة صارت معي ومع فريقي، قصة فيها سهر وتعب، بس نهايتها كانت سعيدة وعلّمتنا كثير. قصة عن كيف كنا غرقانين في بحر من الـ JSON البطيء، وكيف ظهرلنا طوق نجاة اسمه gRPC.

من ذاكرة أبو عمر: ليلة لا تُنسى مع وحش الـ JSON

بتذكرها زي كأنها مبارح. كانت ليلة خميس، وأنا وفريقي كنا بنشتغل على إطلاق ميزة جديدة في نظامنا المبني على الخدمات المصغرة (Microservices). كل شي كان ماشي تمام في بيئة التطوير، الاختبارات كلها ناجحة، والقهوة شغالة. أطلقنا الميزة الجديدة، وبعدها بكم ساعة، بلشت توصلنا التنبيهات… “System is slow”، “High latency detected”، “Users are complaining”.

يا لطيف! نزل علينا الخبر زي الصاعقة. فتحنا الـ dashboards، وشفنا الكارثة بعيوننا. وحدة من خدماتنا الأساسية، خلينا نسميها “خدمة المستخدمين”، كانت بتستجيب ببطء شديد جداً. والأسوأ، إنها كانت بتسبب تأثير الدومينو، وكل الخدمات اللي بتعتمد عليها صارت بطيئة كمان.

قعدنا ساعات طويلة نحلل المشكلة. بالأول فكرنا المشكلة في قاعدة البيانات، بعدين فكرنا في الكود نفسه. بس بعد حفر وتنقيب، اكتشفنا إنه المشكلة الحقيقية كانت في الاتصال بين “خدمة الطلبات” و”خدمة المستخدمين”. خدمة الطلبات كانت بتطلب بيانات عدد كبير من المستخدمين دفعة واحدة، وخدمة المستخدمين كانت بترجّعلها ملف JSON ضخم جداً. حجمه كان بيوصل للميغابايتات في بعض الأحيان!

كنا زي اللي بحاول يبعت حمولة شاحنة كبيرة من خلال ماسورة مي صغيرة. عملية تحويل البيانات لـ JSON (Serialization) على السيرفر، ونقلها عبر الشبكة، وبعدين تحليلها (Deserialization) على العميل… كل هاد كان بياكل وقت وموارد بشكل مش طبيعي. وقتها قلت للفريق، “يا جماعة، الحكي هاد ما بنفع. لازم نلاقي حل جذري لمشكلة الاتصال هاي، وإلا رح نضل نطفّي حرايق”. ومن هون بلشت رحلتنا مع gRPC.

ما المشكلة أصلاً في REST و JSON؟

قبل ما نحكي عن المنقذ gRPC، خلينا نفهم عدونا الأول. بصراحة، REST و JSON مش أعداء، هم أصدقاء خدمونا سنين طويلة، ولسه بنستخدمهم في أماكن كثيرة. لكن لكل شي حدود. لما يتعلق الأمر بالاتصال عالي الأداء بين عشرات أو مئات الخدمات المصغرة داخل شبكتك الخاصة (East-West traffic)، بتبدأ مشاكلهم تظهر.

الثرثرة الزائدة (Verbose & Chatty)

الـ JSON مصمم ليكون مقروء للبشر، وهذا شيء ممتاز. لكن هاي الميزة بتصير عيب لما الآلات تحكي مع بعض. كل طلب واستجابة بتحتوي على أسماء الحقول (keys) مكررة. تخيل عندك قائمة من 1000 مستخدم، وكل مستخدم عنده حقل “user_id”. كلمة “user_id” رح تتكرر 1000 مرة في نفس الرسالة! هذا هدر كبير لحجم البيانات المنقولة.

الـ Serialization والـ Deserialization البطيء

تحويل كائن (object) في لغة البرمجة إلى نص JSON، والعكس، هي عملية تستهلك من وقت المعالج (CPU). صحيح إنها سريعة في الأحجام الصغيرة، لكن لما تتعامل مع بيانات ضخمة ومعقدة، هاي العملية بتصير عنق زجاجة (bottleneck) واضح ومؤثر على الأداء.

انعدام العقود الصارمة (Lack of Strict Contracts)

في عالم REST، العقد بين العميل والسيرفر غالباً ما يكون مجرد توثيق (documentation) مكتوب على جنب (باستخدام أدوات مثل OpenAPI/Swagger). من السهل جداً على مطور في فريق معين إنه يغير اسم حقل أو نوعه، ويكسر كل الخدمات اللي بتعتمد عليه بدون ما يحس. ما في شي بيجبر الطرفين على الالتزام بنفس “اللغة” بشكل صارم على مستوى الكود.

مرحباً بـ gRPC: المنقذ الذي لم نكن نعلم أننا بحاجته

بعد ليلتنا الطويلة مع وحش الـ JSON، بلشنا نبحث عن بدائل. وهون تعرفنا على gRPC. في البداية، كان في شوية تردد من الفريق، “شو هاد الأشي الجديد؟”، “رح ياخد وقت نتعلمه”، “خلينا على اللي بنعرفه”. بس أنا كنت مصرّ، وعملنا إثبات مفهوم (Proof of Concept) صغير، والنتائج كانت مذهلة لدرجة أقنعت الجميع.

ما هو gRPC؟ ببساطة يا جماعة

gRPC هو اختصار لـ “gRPC Remote Procedure Call”. هو إطار عمل (framework) مفتوح المصدر وعالي الأداء طورته جوجل. باختصار، بيسمحلك تستدعي دالّة (function) موجودة على سيرفر بعيد كأنها دالّة محلية عندك في الكود، وبشكل فعال جداً.

الأسلحة السرية: Protocol Buffers و HTTP/2

قوة gRPC الحقيقية تكمن في طبقتين أساسيتين بيعتمد عليهم:

  • Protocol Buffers (Protobuf): هاي هي لغة gRPC. بدل ما تستخدم JSON النصي، بتستخدم Protobuf، وهو صيغة ثنائية (binary) لعملية الـ serialization. بتعرّف شكل البيانات (schema) في ملف خاص اسمه .proto، ومنه بتقدر تولّد كود العميل والسيرفر بلغات برمجة مختلفة (Go, Java, Python, C#, …). لأنه ثنائي، حجمه أصغر بكثير من JSON وسرعة تحليله فائقة.
  • HTTP/2: بينما تستخدم معظم واجهات REST بروتوكول HTTP/1.1، يعمل gRPC فوق HTTP/2. هذا بيعطيه مزايا خارقة مثل:
    • Multiplexing: إمكانية إرسال عدة طلبات واستجابات على نفس الاتصال (connection) في نفس الوقت، بدون ما واحد يblok التاني.
    • Streaming: دعم أصيل للبث الثنائي الاتجاه (bidirectional streaming)، يعني السيرفر والعميل بيقدروا يبعتوا لبعض سيل من الرسائل بشكل مستمر.
    • Header Compression: ضغط الـ headers لتقليل حجم البيانات المنقولة.

لنُشمّر عن سواعدنا: مثال عملي (Go و gRPC)

الحكي النظري حلو، بس خلينا نشوف الكود. لنفترض بدنا نبني خدمة بسيطة بترسل تحية. هاي هي الخطوات:

الخطوة الأولى: تعريف الخدمة بملف .proto

أول شي، بنعمل ملف اسمه greeting/greeting.proto وبنعرّف فيه شكل الرسائل والخدمة نفسها.

// greeting/greeting.proto
syntax = "proto3";

package greeting;

option go_package = "greetingpb";

// الخدمة اللي بتقدم التحية
service Greeter {
  // دالّة بسيطة بترسل تحية
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}

// الرسالة اللي بيحتوي عليها الطلب
message HelloRequest {
  string name = 1;
}

// الرسالة اللي بيحتوي عليها الرد
message HelloResponse {
  string message = 1;
}

لاحظ كيف العقد (contract) واضح ومحدد بشكل صارم. نوع البيانات، أسماء الحقول، كل شي معرف. ما في مجال للخطأ.

الخطوة الثانية: توليد الكود (السحر يبدأ هنا)

باستخدام مترجم Protobuf (protoc)، بنقدر نولّد كود السيرفر والعميل بلغة Go (أو أي لغة أخرى تدعمها).

# أمر لتوليد كود Go من ملف الـ proto
protoc --go_out=. --go_opt=paths=source_relative 
    --go-grpc_out=. --go-grpc_opt=paths=source_relative 
    greeting/greeting.proto

هذا الأمر رح يولّد ملفات Go بتحتوي على كل الكود اللازم للـ serialization والـ networking. إنت ما عليك إلا إنك تكتب المنطق البرمجي للخدمة.

الخطوة الثالثة: كتابة السيرفر (Server)

الآن، نكتب المنطق الفعلي لدالّة SayHello في ملف server/main.go.

// server/main.go
package main

import (
    "context"
    "fmt"
    "log"
    "net"

    pb "path/to/your/greetingpb" // استيراد الكود المولّد
    "google.golang.org/grpc"
)

// تعريف الـ struct الخاص بالسيرفر
type server struct{
    pb.UnimplementedGreeterServer // للتوافقية المستقبلية
}

// تطبيق دالّة SayHello من الـ interface المولّد
func (*server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
    log.Printf("Received a request with name: %v", req.GetName())
    message := "أهلاً وسهلاً يا " + req.GetName()
    res := &pb.HelloResponse{
        Message: message,
    }
    return res, nil
}

func main() {
    lis, err := net.Listen("tcp", "0.0.0.0:50051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }

    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})

    log.Println("Server is running on port 50051...")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}

الخطوة الرابعة: كتابة العميل (Client)

وأخيراً، نكتب العميل اللي رح يستدعي هاي الخدمة في ملف client/main.go.

// client/main.go
package main

import (
    "context"
    "log"

    pb "path/to/your/greetingpb"
    "google.golang.org/grpc"
)

func main() {
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Could not connect: %v", err)
    }
    defer conn.Close()

    c := pb.NewGreeterClient(conn)

    req := &pb.HelloRequest{Name: "أبو عمر"}

    res, err := c.SayHello(context.Background(), req)
    if err != nil {
        log.Fatalf("Error while calling SayHello RPC: %v", err)
    }

    log.Printf("Response from server: %s", res.GetMessage())
}

شوف ما أبسط الموضوع! العميل استدعى c.SayHello(...) كأنها دالّة محلية عادية، وكل تعقيدات الشبكة والـ serialization تمت معالجتها خلف الكواليس بواسطة gRPC.

النتائج على أرض الواقع: قبل وبعد gRPC

بعد ما طبقنا gRPC على الخدمتين الحساستين في نظامنا، النتائج كانت مبهرة بكل معنى الكلمة:

  • سرعة الاستجابة (Latency): متوسط زمن الاستجابة بين الخدمتين نزل من حوالي 200ms في أوقات الذروة إلى أقل من 30ms. تحسن بنسبة تفوق 85%!
  • استخدام الشبكة (Bandwidth): حجم البيانات المنقولة انخفض بشكل كبير. رسالة كانت بحجم 1MB بصيغة JSON، صارت أقل من 200KB بصيغة Protobuf.
  • استهلاك المعالج (CPU): لاحظنا انخفاض في استهلاك الـ CPU على كلا الخدمتين، لأن عملية الـ serialization/deserialization الثنائية أكفأ بكثير.

نصائح من قلب الميدان (من أبو عمر شخصياً)

بعد ما خضنا هاي التجربة، تعلمت كم شغلة بحب أشاركها معكم:

  1. لا ترمي كل شيء دفعة واحدة: الانتقال لـ gRPC لا يعني إنك لازم تعيد كتابة كل خدماتك. ابدأ بالخدمات الأكثر حساسية للأداء، اللي فيها اتصال كثير (chatty services). ابدأ صغير، اثبت النجاح، وبعدين توسع.
  2. الـ API Gateway هو صديقك: gRPC ممتاز للاتصال الداخلي بين الخدمات. لكن المتصفحات والتطبيقات الخارجية لسه بتحكي REST/JSON. استخدم API Gateway مثل Envoy أو Traefik اللي بيقدر يستقبل طلبات HTTP/JSON من الخارج ويترجمها لطلبات gRPC للخدمات الداخلية.
  3. استثمر في الـ Tooling: تعلم كيف تستخدم أدوات مثل grpcurl (زي curl بس لـ gRPC) و BloomRPC لتجربة واجهات gRPC واختبارها بسهولة.
  4. فكر بالـ Streaming: قوة gRPC الحقيقية تظهر في الـ streaming. إذا عندك حالة استخدام تحتاج إرسال أو استقبال تدفق من البيانات (مثل تحديثات حية، رفع ملفات كبيرة)، gRPC هو الخيار الأمثل.

الخلاصة: هل gRPC هو الحل لكل شيء؟

لا، وبكل صراحة. gRPC ليس الرصاصة الفضية اللي بتحل كل المشاكل. REST/JSON ما زال خيار ممتاز جداً، وربما الأفضل، للواجهات العامة (Public APIs) اللي بيتعامل معها مطورين من خارج شركتك، أو للتطبيقات البسيطة اللي ما بتحتاج أداء فائق.

الفكرة هي أن تعرف أدواتك جيداً، وتعرف متى تستخدم كل أداة. gRPC أداة قوية جداً في صندوق عدة المطور، خصوصاً في عالم الخدمات المصغرة المعقد والمتشابك. لما يكون الأداء والفعالية هما الأولوية القصوى في الاتصال الداخلي بين خدماتك، gRPC هو صديقك الذي لن يخذلك.

أتمنى تكون القصة والتفاصيل هاي فادتكم. لا تخافوا من تجربة التقنيات الجديدة، لأنها ممكن تكون هي طوق النجاة اللي بتدوروا عليه. يلا، شدّوا حيلكم! 🚀

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

تجربة المستخدم والابداع البصري

كان تطبيقنا جداراً منيعاً: كيف أنقذتنا ‘إمكانية الوصول’ من جحيم استبعاد المستخدمين؟

أشارككم قصة حقيقية من مسيرتي كمطور، قصة عن تطبيق "مثالي" بنيناه وكان في الحقيقة جداراً منيعاً أمام فئة كبيرة من المستخدمين. هذه المقالة هي رحلتنا...

20 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

كانت تطبيقاتنا تمطر قاعدة البيانات بالاستعلامات: كيف أنقذنا ‘التحميل الجشع’ (Eager Loading) من جحيم مشكلة N+1؟

في هذه المقالة، أشارككم قصة حقيقية عن كيفية تسبب مشكلة N+1 بكارثة أداء في أحد مشاريعنا، وكيف كان "التحميل الجشع" (Eager Loading) هو طوق النجاة....

19 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت فاتورتنا السحابية لغزاً شهرياً: كيف أنقذتنا ‘علامات تخصيص التكلفة’ من جحيم الإنفاق المجهول؟

هل تواجهون فاتورة سحابية متضخمة وغامضة كل شهر؟ في هذه المقالة، أشارككم تجربتي كـ "أبو عمر" في ترويض وحش التكاليف المجهولة باستخدام أداة بسيطة وقوية:...

19 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

سيرتي الذاتية في سلة المهملات الرقمية: كيف هزمتُ روبوتات التوظيف (ATS) بهندسة الكلمات المفتاحية

كانت طلباتي الوظيفية تذهب إلى ثقب أسود رقمي دون أي رد. في هذه المقالة، أسرد لكم قصتي مع أنظمة تتبع المتقدمين (ATS) وكيف أنقذتني هندسة...

19 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت طلبات المستخدمين تُجمّد تطبيقنا: كيف أنقذتنا ‘طوابير الرسائل’ (Message Queues) من جحيم المعالجة المتزامنة؟

أشارككم قصة حقيقية عن اليوم الذي كاد فيه تطبيقنا أن ينهار تحت ضغط المستخدمين، وكيف كانت "طوابير الرسائل" (Message Queues) هي طوق النجاة. اكتشفوا معنا...

19 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

الصيرفة المفتوحة (Open Banking): كيف أنقذتنا واجهات الـ API من جحيم تجميع بيانات العملاء يدويًا؟

أروي لكم حكايتنا، أنا أبو عمر وفريقي، وكيف انتقلنا من الغرق في بحر من ملفات CSV وبيانات العملاء المبعثرة، إلى عالم من الكفاءة والابتكار بفضل...

19 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

من خوادم “ندفات الثلج” إلى بنية صخرية: كيف أنقذتنا البنية التحتية ككود (IaC) من جحيم الإعداد اليدوي

أشارككم قصة حقيقية عن معاناة فريقنا مع الخوادم "الفريدة" وكيف كانت "البنية التحتية ككود" (IaC) طوق النجاة. في هذه المقالة، نستكشف مفهوم IaC وأدواته مثل...

19 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كان الصمت يصم الآذان: كيف أنقذت ‘السلامة النفسية’ فريقنا من جحيم الأخطاء المدفونة؟

أشارككم قصة من قلب المعركة البرمجية، عن اجتماع كاد أن يودي بمشروعنا بسبب الصمت المطبق. هذه المقالة تشرح كيف غيّر مفهوم "السلامة النفسية" ثقافة فريقنا...

19 مايو، 2026 قراءة المزيد
البودكاست