Na
primeira parte de nossa série de artigos "Escrevendo um blog sobre microsserviços" , descrevemos uma abordagem geral para resolver o problema.
Agora é a vez do API Gateway ou GW API.
Em nossa API GW c
ptimofeev , implementamos as seguintes funções:
- Convertendo solicitações REST em solicitações de gRPC e vice-versa.
- Solicitar log.
- Solicitar autenticação
- Designação de um ID de Rastreio para cada solicitação para posterior transferência entre microsserviços ao longo de toda a cadeia de execução da solicitação.
Então vamos lá ...
Para implementar a função de conversão REST / gRPC, usaremos a biblioteca gosh
grpc-gateway .
Além disso, no protofile de cada microsserviço que queremos publicar no REST, você precisa adicionar uma descrição da opção na seção de descrição das interfaces de serviço. Na verdade, ele especifica o caminho e o método pelo qual o acesso REST será executado.
Com base nessas informações, o script de geração de código (./bin/protogen.sh) criará o código do servidor gRPC (no diretório microservice), o cliente gRPC (no diretório api-gw) e gerará a documentação mais recente da API (no formato {{service name}}. swagger.json)
Em seguida, precisamos escrever o código HTTP Proxy, que por um lado será um servidor HTTP (para processar solicitações REST) e, por outro, será um cliente gRPC para nossos microsserviços (servidores gRPC).
Colocaremos esse código no arquivo ./services/api-gw/main.go.
Primeiro, na seção de importação, conectamos bibliotecas clientes aos nossos microsserviços
(protogen.sh os gerou para nós):
import ( … userService "./services/user/protobuf" postService "./services/post/protobuf" commentService "./services/comment/protobuf" categoryService "./services/category/protobuf" …
A seguir, indicamos os endereços e portas nas quais nossos serviços gRPC “travam” (pegamos os valores das variáveis de ambiente):
var (
E, finalmente, implementamos o próprio proxy HTTP:
func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux()
Ao configurar a conexão com microsserviços, usamos a opção grpc.WithUnaryInterceptor (AccessLogInterceptor), na qual passamos a função AccessLogInterceptor como parâmetro. Isso nada mais é do que uma implementação da camada de middleware, ou seja, a função AccessLogInterceptor será executada a cada chamada gRPC do microsserviço filho.
…
Por sua vez, na função AccessLogInterceptor, já implementamos mecanismos de autenticação, log e geração de TraceId.
Se o atributo de autorização foi especificado no cabeçalho na solicitação de entrada (REST), analisamos e validamos na função CheckGetJWTToken, que retorna um erro ou, se bem-sucedido, retorna UserId e 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"]) } }
Em seguida, formamos o TraceId e o agrupamos com UserId e UserRole no contexto da chamada e realizamos a chamada gRPC do nosso microsserviço.
E, finalmente, escrevemos um evento de chamada de serviço no log.
msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", method,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg)
Outro processador de middleware está "travando" nas respostas de métodos específicos (SignIn, SignUp) do serviço do Usuário. Esse manipulador intercepta respostas gRPC, pega as respostas UserID e UserRole, converte-as em
JWT Token e as fornece (JWT Token) na resposta REST como o cabeçalho de atributo "Authorization". O código do middleware descrito é implementado no lado do cliente gRPC no arquivo ./api-gw/services/user/protobuf/functions.go.
Conectamos o manipulador de respostas.
func init() {
Um exemplo é o manipulador de respostas SignIn (o manipulador SignUp é semelhante).
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) {
Para continuar ...
Sim, a demonstração do projeto pode ser vista
aqui e o código fonte está
aqui .