Im
ersten Teil unserer Artikelserie „Schreiben eines Blogs über Microservices“ haben wir einen allgemeinen Ansatz zur Lösung des Problems beschrieben.
Jetzt ist das API-Gateway oder die GW-API an der Reihe.
In unserer c
ptimofeev GW-API implementieren wir die folgenden Funktionen:
- Konvertieren von REST-Anforderungen in gRPC-Anforderungen und umgekehrt.
- Protokollierung anfordern.
- Authentifizierung anfordern
- Zuweisung einer Trace-ID zu jeder Anforderung für die weitere Übertragung zwischen Mikrodiensten entlang der gesamten Anforderungsausführungskette.
Also lass uns gehen ...
Um die REST / gRPC-Konvertierungsfunktion zu implementieren, verwenden wir die
gosh library
grpc-gateway .
Außerdem müssen Sie im Protofile jedes Mikrodienstes, den wir auf REST veröffentlichen möchten, eine Optionsbeschreibung im Beschreibungsabschnitt der Dienstschnittstellen hinzufügen. Es gibt tatsächlich den Pfad und die Methode an, mit denen der REST-Zugriff ausgeführt wird.
Basierend auf diesen Informationen erstellt das Codegenerierungsskript (./bin/protogen.sh) den gRPC-Servercode (im Microservice-Verzeichnis), den gRPC-Client (im api-gw-Verzeichnis) und generiert die neueste API-Dokumentation (im Format {{Dienstname}}). swagger.json)
Als nächstes müssen wir HTTP-Proxy-Code schreiben, der einerseits ein HTTP-Server (zur Verarbeitung von REST-Anforderungen) und andererseits ein gRPC-Client für unsere Microservices (gRPC-Server) ist.
Wir werden diesen Code in die Datei ./services/api-gw/main.go einfügen.
Zunächst verbinden wir im Importbereich Client-Bibliotheken mit unseren Microservices
(protogen.sh hat sie für uns generiert):
import ( … userService "./services/user/protobuf" postService "./services/post/protobuf" commentService "./services/comment/protobuf" categoryService "./services/category/protobuf" …
Als nächstes geben wir die Adressen und Ports an, an denen unsere gRPC-Dienste "hängen" (wir nehmen die Werte aus den Umgebungsvariablen):
var (
Und schließlich implementieren wir den HTTP-Proxy selbst:
func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux()
Beim Einrichten der Verbindung zu Microservices verwenden wir die Option grpc.WithUnaryInterceptor (AccessLogInterceptor), an die wir die AccessLogInterceptor-Funktion als Parameter übergeben. Dies ist nichts weiter als eine Implementierung der Middleware-Schicht, d.h. Die AccessLogInterceptor-Funktion wird bei jedem gRPC-Aufruf des untergeordneten Mikrodienstes ausgeführt.
…
Im Gegenzug implementieren wir in der AccessLogInterceptor-Funktion bereits Authentifizierungs-, Protokollierungs- und TraceId-Generierungsmechanismen.
Wenn das Berechtigungsattribut in der eingehenden Anforderung (REST) im Header angegeben wurde, analysieren und validieren wir es in der Funktion CheckGetJWTToken, die entweder einen Fehler zurückgibt oder bei Erfolg UserId und UserRole zurückgibt.
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"]) } }
Als nächstes bilden wir TraceId und verpacken es zusammen mit UserId und UserRole im Aufrufkontext und führen den gRPC-Aufruf unseres Microservices durch.
Und schließlich schreiben wir ein Serviceabrufereignis in das Protokoll.
msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", method,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg)
Ein anderer Middleware-Prozessor hängt an den Antworten bestimmter Methoden (SignIn, SignUp) des Benutzerdienstes. Dieser Handler fängt gRPC-Antworten ab, nimmt die UserID- und UserRole-Antwort auf, konvertiert sie in ein
JWT-Token und gibt sie (JWT-Token) in der REST-Antwort als Header des Attributs "Authorization" an. Der beschriebene Middleware-Code ist auf der gRPC-Client-Seite in der Datei ./api-gw/services/user/protobuf/functions.go implementiert.
Wir verbinden den Response Handler.
func init() {
Ein Beispiel ist der SignIn-Antworthandler (der SignUp-Handler ist ähnlich).
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) {
Fortsetzung folgt…
Ja, die Demo des Projekts kann hier angesehen
werden , und der Quellcode ist
hier .