Dans la
première partie de notre série d'articles «Rédaction d'un blog sur les microservices», nous avons décrit une approche générale pour résoudre le problème.
C'est maintenant au tour de l'API Gateway ou de l'API GW.
Dans notre API c
ptimofeev GW, nous implémentons les fonctions suivantes:
- Conversion des demandes REST en demandes gRPC et vice versa.
- Journalisation des demandes.
- Demande d'authentification
- Affectation d'un identifiant de trace à chaque demande pour son transfert ultérieur entre les microservices tout au long de la chaîne d'exécution de la demande.
Alors allons-y ...
Pour implémenter la fonction de conversion REST / gRPC, nous utiliserons la bibliothèque gosh
grpc-gateway .
De plus, dans le profil de chaque microservice que nous voulons publier sur REST, vous devez ajouter une description d'option dans la section description des interfaces de service. Il spécifie en fait le chemin et la méthode par lesquels l'accès REST sera effectué.
Sur la base de ces informations, le script de génération de code (./bin/protogen.sh) créera le code du serveur gRPC (dans le répertoire du microservice), le client gRPC (dans le répertoire api-gw) et générera la dernière documentation API (au format {{nom du service}}). swagger.json)
Ensuite, nous devons écrire du code proxy HTTP, qui d'une part sera un serveur HTTP (pour le traitement des demandes REST), et d'autre part ce sera un client gRPC pour nos microservices (serveurs gRPC).
Nous placerons ce code dans le fichier ./services/api-gw/main.go.
Tout d'abord, dans la section importation, nous connectons les bibliothèques clientes à nos microservices
(protogen.sh les a générés pour nous):
import ( … userService "./services/user/protobuf" postService "./services/post/protobuf" commentService "./services/comment/protobuf" categoryService "./services/category/protobuf" …
Ensuite, nous indiquons les adresses et les ports sur lesquels nos services gRPC «se bloquent» (nous prenons les valeurs des variables d'environnement):
var (
Et enfin, nous implémentons le proxy HTTP lui-même:
func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux()
Pour configurer la connexion aux microservices, nous utilisons l'option grpc.WithUnaryInterceptor (AccessLogInterceptor), dans laquelle nous transmettons la fonction AccessLogInterceptor comme paramètre. Ce n'est rien de plus qu'une implémentation de la couche middleware, c'est-à-dire la fonction AccessLogInterceptor sera exécutée avec chaque appel gRPC du microservice enfant.
…
À son tour, dans la fonction AccessLogInterceptor, nous implémentons déjà des mécanismes d'authentification, de journalisation et de génération TraceId.
Si l'attribut d'autorisation a été spécifié dans l'en-tête de la demande entrante (REST), nous l'analysons et le validons dans la fonction CheckGetJWTToken, qui renvoie une erreur ou, en cas de succès, renvoie UserId et 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"]) } }
Ensuite, nous formons TraceId et l'enveloppons avec UserId et UserRole dans le contexte de l'appel et effectuons l'appel gRPC de notre microservice.
Et enfin, nous écrivons un événement d'appel de service dans le journal.
msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", method,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg)
Un autre processeur middleware «accroche» aux réponses de méthodes spécifiques (SignIn, SignUp) du service Utilisateur. Ce gestionnaire intercepte les réponses gRPC, récupère la réponse UserID et UserRole, la convertit en
jeton JWT et la donne (jeton JWT) dans la réponse REST en tant qu'en-tête d'attribut «Autorisation». Le code du middleware décrit est implémenté côté client gRPC dans le fichier ./api-gw/services/user/protobuf/functions.go.
Nous connectons le gestionnaire de réponse.
func init() {
Un exemple est le gestionnaire de réponses SignIn (le gestionnaire SignUp est similaire).
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) {
À suivre ...
Oui, la démo du projet peut être consultée
ici , et le code source est
ici .