एक माइक्रोसॉर्फ़ ब्लॉग लिखना - एपीआई गेटवे का भाग 2

लेखों की हमारी श्रृंखला के पहले भाग में "माइक्रोसॉर्क्स पर एक ब्लॉग लिखना", हमने समस्या को हल करने के लिए एक सामान्य दृष्टिकोण का वर्णन किया।

अब एपीआई गेटवे या जीडब्ल्यू एपीआई की बारी है।

हमारे ग ptimofeev GW एपीआई में, हम निम्नलिखित कार्यों को लागू करते हैं:

  • GRPC अनुरोधों के विपरीत अनुरोध और इसके विपरीत।
  • लॉगिंग का अनुरोध करें।
  • अनुरोध प्रमाणीकरण
  • पूरे अनुरोध निष्पादन श्रृंखला के साथ माइक्रोसिस्टर्स के बीच इसके आगे हस्तांतरण के लिए प्रत्येक अनुरोध के लिए एक ट्रेस आईडी का असाइनमेंट।

तो चलिए जाने ...

REST / gRPC रूपांतरण फ़ंक्शन को लागू करने के लिए, हम gosh पुस्तकालय grpc-gateway का उपयोग करेंगे।

इसके अलावा, प्रत्येक माइक्रोस्पोर के प्रोटोफाइल में जिसे हम REST पर प्रकाशित करना चाहते हैं, आपको सेवा इंटरफेस के विवरण अनुभाग में एक विकल्प विवरण जोड़ना होगा। यह वास्तव में पथ और विधि को निर्दिष्ट करता है जिसके द्वारा REST पहुंच का प्रदर्शन किया जाएगा।

//  Category service CategoryService { //  rpc Create (CreateCategoryRequest) returns (CreateCategoryResponse) { option (google.api.http) = { post: "/api/v1/category" }; } } 

इस जानकारी के आधार पर, कोड जेनरेशन स्क्रिप्ट (./bin/protogen.sh) gRPC सर्वर कोड (microservice डायरेक्टरी में), gRPC क्लाइंट (api-gw डायरेक्टरी में) बनाएगी और लेटेस्ट {{सेवा नाम}} प्रारूप में नवीनतम दस्तावेज़ तैयार करेगी। swagger.json)

इसके बाद, हमें HTTP प्रॉक्सी कोड लिखने की आवश्यकता है, जिसमें एक ओर HTTP सर्वर होगा (REST अनुरोधों को संसाधित करने के लिए), और दूसरी ओर यह हमारे microservices (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" 

अगला, हम उन पते और बंदरगाहों को इंगित करते हैं जिन पर हमारी जीआरपीसी सेवाएं "हैंग" (हम पर्यावरण चर से मान लेते हैं):

 var ( // gRPC services userServerAdress=fmt.Sprintf("%s:%s",os.Getenv("USER_HOST"),os.Getenv("USER_PORT")) postServerAdress=fmt.Sprintf("%s:%s",os.Getenv("POST_HOST"),os.Getenv("POST_PORT")) commentServerAdress=fmt.Sprintf("%s:%s",os.Getenv("COMMENT_HOST"),os.Getenv("COMMENT_PORT")) categoryServerAdress=fmt.Sprintf("%s:%s",os.Getenv("CATEGORY_HOST"),os.Getenv("CATEGORY_PORT")) ) 

और अंत में, हम HTTP प्रॉक्सी को ही लागू करते हैं:

 func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux() //---------------------------------------------------------------- //     gRPC //---------------------------------------------------------------- //   User grpcUserConn, err:=grpc.Dial( userServerAdress, grpc.WithInsecure(), ) if err!=nil{ log.Fatalln("Failed to connect to User service", err) } defer grpcUserConn.Close() err = userService.RegisterUserServiceHandler( context.Background(), grpcGwMux, grpcUserConn, ) if err!=nil{ log.Fatalln("Failed to start HTTP server", err) } //---------------------------------------------------------------- //   Post grpcPostConn, err:=grpc.Dial( postServerAdress, grpc.WithUnaryInterceptor(AccessLogInterceptor), grpc.WithInsecure(), ) if err!=nil{ log.Fatalln("Failed to connect to Post service", err) } defer grpcPostConn.Close() err = postService.RegisterPostServiceHandler( context.Background(), grpcGwMux, grpcPostConn, ) if err!=nil{ log.Fatalln("Failed to start HTTP server", err) } //---------------------------------------------------------------- //   Comment grpcCommentConn, err:=grpc.Dial( commentServerAdress, grpc.WithInsecure(), ) if err!=nil{ log.Fatalln("Failed to connect to Comment service", err) } defer grpcCommentConn.Close() err = commentService.RegisterCommentServiceHandler( context.Background(), grpcGwMux, grpcCommentConn, ) if err!=nil{ log.Fatalln("Failed to start HTTP server", err) } //---------------------------------------------------------------- //   Category grpcCategoryConn, err:=grpc.Dial( categoryServerAdress, grpc.WithInsecure(), ) if err!=nil{ log.Fatalln("Failed to connect to Category service", err) } defer grpcCategoryConn.Close() err = categoryService.RegisterCategoryServiceHandler( context.Background(), grpcGwMux, grpcCategoryConn, ) if err!=nil{ log.Fatalln("Failed to start HTTP server", err) } //---------------------------------------------------------------- //     REST //---------------------------------------------------------------- mux:=http.NewServeMux() mux.Handle("/api/v1/",grpcGwMux) mux.HandleFunc("/",helloworld) fmt.Println("starting HTTP server at "+proxyAddr) log.Fatal(http.ListenAndServe(proxyAddr,mux)) } 

माइक्रोसर्विसेस से कनेक्शन स्थापित करने में, हम grpc। ऑनरीरी इन्टरसेप्टर (AccessLogInterceptor) विकल्प का उपयोग करते हैं, जिसमें हम एक पैरामीटर के रूप में AccessLogInterceptor फ़ंक्शन को पास करते हैं। यह मिडिलवेयर परत के कार्यान्वयन से ज्यादा कुछ नहीं है, अर्थात AccessLogInterceptor फ़ंक्शन को बाल microservice के प्रत्येक gRPC कॉल के साथ निष्पादित किया जाएगा।

 //---------------------------------------------------------------- //   Post grpcPostConn, err:=grpc.Dial( … grpc.WithUnaryInterceptor(AccessLogInterceptor), … ) 

बदले में, 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 के साथ इसे एक साथ लपेटते हैं और हमारे microservice के gRPC कॉल को पूरा करते हैं।

 // ID  traceId=fmt.Sprintf("%d",time.Now().UTC().UnixNano()) callContext:=context.Background() mdOut:=metadata.Pairs( "trace-id",traceId, "user-id",userId, "user-role",userRole, ) callContext=metadata.NewOutgoingContext(callContext,mdOut) err:=invoker(callContext,method,req,reply,cc, opts...) 

और अंत में, हम लॉग में एक सेवा कॉल घटना लिखते हैं।

 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 टोकन में परिवर्तित करता है और इसे (JWT टोकन) REST प्रतिक्रिया में "प्राधिकरण" विशेषता हैडर के रूप में देता है। वर्णित मिडलवेयर कोड फ़ाइल में gRPC क्लाइंट साइड पर लागू किया गया है ।/api-gw/services/user/protobuf/functions.go।

हम प्रतिक्रिया हैंडलर को जोड़ते हैं।

 func init() { //     SignIn forward_UserService_SignIn_0 = forwardSignIn //     SignUp forward_UserService_SignUp_0 = forwardSignUp } 

एक उदाहरण SignIn प्रतिक्रिया हैंडलर है (साइनअप हैंडलर समान है)।

 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) { // proto.Message  SignInResponse signInResponse:=&SignInResponse{} signInResponse.XXX_Merge(resp) token,err:=GetJWTToken(signInResponse.Slug,signInResponse.Role) if err!=nil{ http.Error(w, fmt.Sprintf("%v",err), http.StatusUnauthorized) return } w.Header().Set("authorization", token) runtime.ForwardResponseMessage(ctx, mux, marshaler, w, req, resp, opts...) } 

जारी रखने के लिए ...

हां, परियोजना का डेमो यहां देखा जा सकता है , और यहां स्रोत कोड।

Source: https://habr.com/ru/post/hi473516/


All Articles