في المشهد الحديث للأنظمة الموزعة، تعتمد الخدمات المصغرة اعتماداً كبيراً على التواصل الفعال والآمن. أصبحت تقنية gRPC من Google المعيار الفعلي للتواصل الداخلي بين الخدمات نظراً لأدائها العالي، ونوعيتها القوية، ودعمها الأصلي لـ HTTP/2. ومع ذلك، فإن بناء بنية خدمات مصغرة مرنة يتطلب أكثر من مجرد تعريف رسائل protobuf. فهو يتطلب معالجة شاملة للاهتمامات المتقاطعة مثل الأمان، والرصد، ومعالجة الأخطاء بشكل متسق عبر جميع الخدمات.
هنا يأتي دور معطلات gRPC. تعمل هذه المعطلات كوسيط (middleware)، مما يتيح لك الربط مع دورة حياة خادم وعميل gRPC، مما يمكّنك من حقن منطق للمصادقة، والتسجيل، والقياسات، وإعادة المحاولة دون إرباك منطق الأعمال الأساسي. في هذا المنشور، سنستكشف كيفية تطبيق معطلين حرجين في Go: أحدهما للمصادقة والآخر للتسجيل التفصيلي للطلبات.
فهم معطلات gRPC
معطلات gRPC هي دوال تحيط باستدعاءات RPC. تعمل قبل تنفيذ المعالج الفعلي (في جانب الخادم) أو بعد أن يقوم العميل بإرسال الطلب (في جانب العميل). يشبه هذا النمط استخدام الوسيط في أطر عمل HTTP مثل Gin أو Echo، لكنه يعمل على طبقة النقل الخاصة بـ RPC.
بالنسبة لاستدعاءات RPC أحادية الجانب في جانب الخادم (استجابة طلب قياسية)، تبدو توقيع الدالة للمعطل في Go كالتالي:
func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// منطق ما قبل المعالج
resp, err := handler(ctx, req)
// منطق ما بعد المعالج
return resp, err
}
من خلال تسلسل عدة معطلات، يمكنك إنشاء خط أنابيب قوي. على سبيل المثال، قد يكون لديك معطل تسجيل يحيط بمعطل مصادقة، والذي بدوره يحيط بمنطق الأعمال الفعلي الخاص بك.
تطبيق معطل المصادقة
الأمان أمر بالغ الأهمية. النمط الشائع هو التحقق من صحة رمز JWT (رمز ويب JSON) الممرر في بيانات التعريف (metadata) لطلب gRPC قبل السماح بالوصول إلى النقاط النهائية المحمية. يوضح المعطل التالي كيفية استخراج الرمز من رأس Authorization، والتحقق من صحته، وحقن مطالبات المستخدم في السياق للمعالجات اللاحقة.
func AuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "missing metadata")
}
tokens := md.Get("authorization")
if len(tokens) == 0 {
return nil, status.Error(codes.Unauthenticated, "missing token")
}
token := tokens[0]
// نفترض أن validateJWT تعيد المطالبات أو خطأ
claims, err := validateJWT(token)
if err != nil {
return nil, status.Error(codes.Unauthenticated, "invalid token")
}
// حقن معرف المستخدم في السياق
ctx = context.WithValue(ctx, "userID", claims.UserID)
return handler(ctx, req)
}
يضمن هذا النهج أن كل نقطة نهاية محمية يمكنها استرداد المستخدم المصادق عليه ببساطة من خلال استدعاء ctx.Value("userID")، مما يبقي منطق الأعمال نظيفاً ومركزاً.
تطبيق معطل تسجيل الطلبات
الرصد لا يقل أهمية عن الأمان. في بنية الخدمات المصغرة، يعد تتبع تدفق الطلبات عبر الخدمات المختلفة أمراً أساسياً لتصحيح الأخطاء ومراقبة الأداء. يمكن لمعطل التسجيل التقاط وقت البدء، واسم الطريقة، وحمولة الطلب (المهذبة)، وحالة الاستجابة.
func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
log.Printf("Started %s", info.FullMethod)
resp, err := handler(ctx, req)
if err != nil {
log.Printf("Finished %s in %v with error: %v", info.FullMethod, time.Since(start), err)
return resp, err
}
log.Printf("Finished %s in %v", info.FullMethod, time.Since(start))
return resp, nil
}
يوفر هذا المعطل البسيط رؤى فورية حول المدة التي تستغرقها نقاط النهاية الخاصة بك للرد، ويساعد في تحديد الطلبات الفاشلة بسرعة. في بيئات الإنتاج، من المرجح أن تقوم بتحسين ذلك من خلال التكامل مع مكتبات التسجيل المهيكلة مثل Zap أو Logrus وإرسال المقاييس إلى Prometheus.
جمع كل شيء معاً
لاستخدام هذه المعطلات، قم بتسجيلها عند تهيئة خادم gRPC الخاص بك. يمكنك تمريرها كخيارات إلى grpc.NewServer(). الترتيب مهم: المعطل الأول في القائمة هو الأول الذي يتم تنفيذه، مما يعني أنه يحيط بالمعطل الثاني، وهكذا.
server := grpc.NewServer(
grpc.UnaryInterceptor(LoggingInterceptor),
grpc.UnaryInterceptor(AuthInterceptor),
)
الخاتمة
يُعد تطبيق معطلات gRPC في Go ممارسة جيدة لبناء خدمات مصغرة قابلة للصيانة والآمنة. من خلال فصل الاهتمامات مثل المصادقة والتسجيل إلى معطلات قابلة لإعادة الاستخدام، تحافظ على منطق التطبيق الأساسي نظيفاً وتضمن سلوكاً متسقاً عبر جميع خدماتك. سواء كنت تؤمن واجهة برمجة التطبيقات الخاصة بك باستخدام رموز JWT أو تراقب الأداء باستخدام سجلات تفصيلية، توفر المعطلات الآلية المرنة والقوية اللازمة لمعالجة الاهتمامات المتقاطعة بفعالية.