En la
primera parte de nuestra serie de artículos "Escribir un blog en microservicios" , describimos un enfoque general para resolver el problema.
Ahora es el turno de API Gateway o GW API.
En nuestra API c
ptimofeev GW, implementamos las siguientes funciones:
- Conversión de solicitudes REST a solicitudes gRPC y viceversa.
- Solicitar registro.
- Solicitar autenticación
- Asignación de una ID de seguimiento a cada solicitud para su posterior transferencia entre microservicios a lo largo de toda la cadena de ejecución de la solicitud.
Entonces vamos ...
Para implementar la función de conversión REST / gRPC, utilizaremos la biblioteca gosh
grpc-gateway .
Además, en el protofile de cada microservicio que queremos publicar en REST, debe agregar una descripción de la opción en la sección de descripción de las interfaces de servicio. En realidad, especifica la ruta y el método por el cual se realizará el acceso REST.
Según esta información, el script de generación de código (./bin/protogen.sh) creará el código del servidor gRPC (en el directorio de microservicios), el cliente gRPC (en el directorio api-gw) y generará la última documentación de la API (en el formato {{nombre de servicio}}. swagger.json)
A continuación, debemos escribir el código de Proxy HTTP, que por un lado será un servidor HTTP (para procesar solicitudes REST) y, por otro lado, será un cliente gRPC para nuestros microservicios (servidores gRPC).
Colocaremos este código en el archivo ./services/api-gw/main.go.
Primero, en la sección de importación, conectamos las bibliotecas del cliente a nuestros microservicios
(protogen.sh los generó para nosotros):
import ( … userService "./services/user/protobuf" postService "./services/post/protobuf" commentService "./services/comment/protobuf" categoryService "./services/category/protobuf" …
A continuación, indicamos las direcciones y puertos en los que nuestros servicios gRPC se "cuelgan" (tomamos los valores de las variables de entorno):
var (
Y finalmente, implementamos el Proxy HTTP en sí:
func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux()
Al configurar la conexión a microservicios, utilizamos la opción grpc.WithUnaryInterceptor (AccessLogInterceptor), en la que pasamos la función AccessLogInterceptor como parámetro. Esto no es más que una implementación de la capa de middleware, es decir la función AccessLogInterceptor se ejecutará con cada llamada gRPC del microservicio secundario.
…
A su vez, en la función AccessLogInterceptor, ya implementamos mecanismos de autenticación, registro y generación de TraceId.
Si el atributo de autorización se especificó en el Encabezado en la solicitud entrante (REST), lo analizamos y lo validamos en la función CheckGetJWTToken, que devuelve un error o, si tiene éxito, devuelve UserId y 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"]) } }
A continuación, formamos TraceId y lo agrupamos con UserId y UserRole en el contexto de la llamada y llevamos a cabo la llamada gRPC de nuestro microservicio.
Y finalmente, escribimos un evento de llamada de servicio en el registro.
msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", method,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg)
Otro procesador de middleware está "colgando" de las respuestas de métodos específicos (SignIn, SignUp) del servicio de usuario. Este controlador intercepta las respuestas de gRPC, recoge la respuesta de UserID y UserRole, la convierte en
JWT Token y la entrega (JWT Token) en la respuesta REST como el encabezado de atributo "Autorización". El código de middleware descrito se implementa en el lado del cliente gRPC en el archivo ./api-gw/services/user/protobuf/functions.go.
Conectamos el manejador de respuestas.
func init() {
Un ejemplo es el controlador de respuesta SignIn (el controlador SignUp es similar).
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) {
Continuará ...
Sí, la demostración del proyecto se puede ver
aquí , y el código fuente está
aquí .