في
الجزء الأول من سلسلة مقالاتنا "كتابة مدونة على Microservices" ، وصفنا مقاربة عامة لحل المشكلة.
الآن جاء دور API Gateway أو GW API.
في واجهة برمجة تطبيقات c
ptimofeev GW الخاصة بنا ، نقوم بتنفيذ الوظائف التالية:
- تحويل طلبات REST إلى طلبات gRPC والعكس.
- طلب تسجيل.
- طلب المصادقة
- تعيين معرف تتبع لكل طلب لنقله بين خدمات micros على امتداد سلسلة تنفيذ الطلب بالكامل.
لذلك دعونا نذهب ...
لتنفيذ وظيفة تحويل REST / gRPC ،
سنستخدم بوابة gpc الخاصة بمكتبة gosh.
علاوة على ذلك ، في الملف الأولي لكل خدمة ميكروية نريد نشرها على REST ، تحتاج إلى إضافة وصف خيار في قسم الوصف بواجهات الخدمة. وهي تحدد بالفعل المسار والأسلوب الذي سيتم به الوصول إلى REST.
استنادًا إلى هذه المعلومات ، سينشئ البرنامج النصي لإنشاء الشفرة (./bin/protogen.sh) رمز خادم gRPC (في دليل الخدمة المصغرة) ، عميل gRPC (في دليل api-gw) وإنشاء أحدث وثائق API (في {{اسم الخدمة}}). swagger.json)
بعد ذلك ، نحتاج إلى كتابة رمز HTTP Proxy ، والذي سيكون من ناحية خادم HTTP (لمعالجة طلبات REST) ، ومن ناحية أخرى ، سيكون عميل gRPC لخدماتنا المصغرة (خوادم gRPC).
سنضع هذا الرمز في الملف ./services/api-gw/main.go.
أولاً ، في قسم الاستيراد ، نقوم بتوصيل مكتبات العملاء إلى خدماتنا المصغرة
(protogen.sh إنشاؤها لهم بالنسبة لنا):
import ( … userService "./services/user/protobuf" postService "./services/post/protobuf" commentService "./services/comment/protobuf" categoryService "./services/category/protobuf" …
بعد ذلك ، نشير إلى العناوين والمنافذ التي "تعلق" خدمات gRPC الخاصة بنا (نأخذ القيم من متغيرات البيئة):
var (
وأخيرًا ، ننفذ وكيل HTTP نفسه:
func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux()
في إعداد الاتصال بخدمات microservices ، نستخدم خيار grpc.WithUnaryInterceptor (AccessLogInterceptor) ، والذي نمر فيه إلى وظيفة AccessLogInterceptor كمعلمة. هذا ليس أكثر من مجرد تطبيق لطبقة الوسيطة ، أي سيتم تنفيذ وظيفة AccessLogInterceptor مع كل مكالمة gRPC لجهاز microservice الفرعي.
…
بدوره ، في وظيفة AccessLogInterceptor ، نطبق بالفعل آليات إنشاء المصادقة وتسجيل الدخول وإنشاء TraceId.
إذا تم تحديد سمة التخويل في رأس الصفحة في الطلب الوارد (REST) ، فسنقوم بتحليلها والتحقق منها في وظيفة CheckGetJWTToken ، التي تُرجع خطأً أو تُرجع UserId و UserRole في حالة نجاحها.
var traceId,userId,userRole string if len(md["authorization"])>0{ tokenString:= md["authorization"][0] if tokenString!=""{ err,token:=userService.CheckGetJWTToken(tokenString) if err!=nil{ return err } userId=fmt.Sprintf("%s",token["UserID"]) userRole=fmt.Sprintf("%s",token["UserRole"]) } }
بعد ذلك ، نقوم بتكوين TraceId ولفه مع UserId و UserRole في سياق المكالمة وننفذ استدعاء gRPC الخاص بالخدمة المصغرة الخاصة بنا.
وأخيراً ، نكتب حدث مكالمة خدمة إلى السجل.
msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", method,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg)
هناك معالج وسيط آخر "معلق" على إجابات طرق محددة (تسجيل الدخول ، تسجيل الدخول) لخدمة المستخدم. يعترض هذا المعالج استجابات gRPC ، ويستلم استجابة UserID و UserRole ، ويحولها إلى
JWT Token ويعطيها (JWT Token) في استجابة REST كرمز للسمة "Authorization". يتم تطبيق رمز الوسيطة الموصوفة على جانب عميل gRPC في الملف ./api-gw/services/user/protobuf/functions.go.
نحن ربط معالج الاستجابة.
func init() {
مثال على ذلك هو معالج استجابة SignIn (يشبه معالج SignUp).
func forwardSignIn(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
أن تستمر ...
نعم ، يمكن الاطلاع على العرض التوضيحي للمشروع
هنا ، والكود المصدري موجود
هنا .