Integrando o Apache APISIX com gRPC-Web
Fei Han
January 25, 2022
Introdução ao gRPC Web
Originalmente desenvolvido pelo Google, o gRPC é um framework de chamada de procedimento remoto de alto desempenho implementado sobre HTTP/2. No entanto, como os navegadores não expõem diretamente o HTTP/2, aplicações Web não podem usar o gRPC diretamente. O gRPC Web é um protocolo padronizado que resolve esse problema.
A primeira implementação do gRPC-web foi lançada em 2018 como uma biblioteca JavaScript, por meio da qual aplicações Web podem se comunicar diretamente com o serviço gRPC. O princípio é criar um pipeline gRPC de ponta a ponta compatível com HTTP/1.1 e HTTP/2. O navegador então envia uma requisição HTTP regular, e um proxy gRPC-Web localizado entre o navegador e o servidor traduz a requisição e a resposta. Semelhante ao gRPC, o gRPC Web usa um contrato pré-definido entre o cliente Web e o serviço gRPC back-end. Os Protocol Buffers são usados para serializar e codificar as mensagens.
Com o gRPC Web, os usuários podem chamar aplicações gRPC back-end diretamente usando um navegador ou cliente Node. No entanto, existem algumas limitações ao usar o gRPC-Web no lado do navegador para chamar serviços gRPC.
- Chamadas de streaming do lado do cliente e streaming bidirecional não são suportadas.
- Chamar serviços gRPC entre domínios requer que o CORS seja configurado no lado do servidor.
- O lado do servidor gRPC deve ser configurado para suportar gRPC-Web, ou um serviço de proxy de terceiros deve estar disponível para traduzir a chamada entre o navegador e o servidor.
Proxy gRPC Web do Apache APISIX
O Apache APISIX suporta o proxy do protocolo gRPC Web por meio de um plugin, que realiza a conversão de protocolo e a codificação de dados quando o gRPC Web se comunica com o gRPC Server no plugin grpc-web
, e seu processo de comunicação é o seguinte.
Cliente gRPC Web -> Apache APISIX (conversão de protocolo e codificação de dados) -> Servidor gRPC
A seguir, um exemplo completo mostrando como construir um Cliente gRPC Web e fazer proxy de requisições gRPC Web através do Apache APISIX. No exemplo a seguir, usaremos Go como o manipulador do servidor gRPC Server e Node como o solicitante do cliente gRPC Web.
Configurar o Protocol Buffer
O primeiro passo é instalar o compilador do Protocol Buffer e os plugins relacionados.
-
Instale
protoc
eproto-grpc-*
.O compilador do Protocol Buffer
protoc
e os pluginsprotoc-gen-go
eprotoc-gen-grpc-web
para gerar código de interface Go, JavaScript e gRPC Web para.proto
precisam ser instalados no seu sistema antes de escrever aplicações do lado do cliente e do servidor.Execute o seguinte script para instalar os componentes acima.
#!/usr/bin/env bash set -ex PROTOBUF_VERSION="3.19.0" wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip unzip protoc-${PROTOBUF_VERSION}-linux-x86_64.zip mv bin/protoc /usr/local/bin/protoc mv include/google /usr/local/include/ chmod +x /usr/local/bin/protoc PROTO_GO_PLUGIN_VER="1.2.0" wget https://github.com/grpc/grpc-go/releases/download/cmd/protoc-gen-go-grpc/v${PROTO_GO_PLUGIN_VER}/protoc-gen-go-grpc.v${PROTO_GO_PLUGIN_VER}.linux.amd64.tar.gz tar -zxvf protoc-gen-go-grpc.v${PROTO_GO_PLUGIN_VER}.linux.amd64.tar.gz mv protoc-gen-go-grpc /usr/local/bin/protoc-gen-go chmod +x /usr/local/bin/protoc-gen-go PROTO_JS_PLUGIN_VER="1.3.0" wget https://github.com/grpc/grpc-web/releases/download/${PROTO_JS_PLUGIN_VER}/protoc-gen-grpc-web-${PROTO_JS_PLUGIN_VER}-linux-x86_64 mv protoc-gen-grpc-web-${PROTO_JS_PLUGIN_VER}-linux-x86_64 /usr/local/bin/protoc-gen-grpc-web chmod +x /usr/local/bin/protoc-gen-grpc-web
-
Crie o arquivo
proto
de exemploSayHello
.// a6/echo.proto syntax = "proto3"; package a6; option go_package = "./;a6"; message EchoRequest { string message = 1; } message EchoResponse { string message = 1; } service EchoService { rpc Echo(EchoRequest) returns (EchoResponse); }
Configurar a aplicação do lado do servidor
-
Gere mensagens brutas Go do lado do servidor e stubs de serviço/cliente.
protoc -I./a6 echo.proto --go_out=plugins=grpc:./a6
-
Implemente a interface do manipulador do lado do servidor.
// a6/echo.impl.go package a6 import ( "errors" "golang.org/x/net/context" ) type EchoServiceImpl struct { } func (esi *EchoServiceImpl) Echo(ctx context.Context, in *EchoRequest) (*EchoResponse, error) { if len(in.Message) <= 0 { return nil, errors.New("message invalid") } return &EchoResponse{Message: "response: " + in.Message}, nil }
-
Arquivo de entrada de execução da aplicação do lado do servidor.
// server.go package main import ( "fmt" "log" "net" "apisix.apache.org/example/a6" "google.golang.org/grpc" ) func main() { lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 50001)) if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() a6.RegisterEchoServiceServer(grpcServer, &a6.EchoServiceImpl{}) if err = grpcServer.Serve(lis); err != nil { log.Fatalf("failed to serve: %s", err) } }
-
Compile e inicie o serviço do lado do servidor.
go build -o grpc-server server.go ./grpc-server
Configurar programas do cliente
-
Gere código
proto
do lado do cliente.Gere mensagens brutas JavaScript do lado do cliente, stubs de serviço/cliente e código de interface para o JavaScript do gRPC Web.
O plugin
proto
para gRPC Web fornece dois modos de geração de código.-
mode=grpcwebtext: O código gerado por padrão envia o payload no formato grpc-web-text.
-
Content-type: application/grpc-web-text
-
Payload usa codificação base64
-
Suporta chamadas unárias e de streaming do servidor
-
mode=grpcweb: envia o payload no formato binário protobuf.
- Content-type: application/grpc-web+proto
- Payload está no formato binário protobuf
- Atualmente, apenas chamadas unárias são suportadas
protoc -I=./a6 echo.proto --js_out=import_style=commonjs:./a6 --grpc-web_out=import_style=commonjs,mode=grpcweb:./a6
-
-
Instale as dependências do lado do cliente.
npm i grpc-web npm i google-protobuf
-
Arquivo de entrada de execução do lado do cliente.
// client.js const {EchoRequest} = require('./a6/echo_pb'); const {EchoServiceClient} = require('./a6/echo_grpc_web_pb'); // conecte-se à entrada do Apache APISIX let echoService = new EchoServiceClient('http://127.0.0.1:9080'); let request = new EchoRequest(); request.setMessage("hello") echoService.echo(request, {}, function (err, response) { if (err) { console.log(err.code); console.log(err.message); } else { console.log(response.getMessage()); } });
-
Estrutura final do projeto
$ tree . ├── a6 │ ├── echo.impl.go │ ├── echo.pb.go │ ├── echo.proto │ ├── echo_grpc_web_pb.js │ └── echo_pb.js ├── client.js ├── server.go ├── go.mod ├── go.sum ├── package.json └── package-lock.json
Após concluir os passos acima, você configurou a aplicação do servidor gRPC Server e a aplicação do cliente gRPC Web, e iniciou a aplicação do servidor, que receberá requisições na porta 50001
.
Configurar o Apache APISIX
Em seguida, basta habilitar o plugin grpc-web
na configuração do plugin de roteamento do Apache APISIX para fazer proxy de requisições gRPC Web.
-
Habilite o plugin de proxy
grpc-web
.O roteamento deve usar correspondência de prefixo (por exemplo,
/* ou /grpc/example/*
), porque o cliente gRPC Web passa o nome do pacote, nome da interface de serviço, nome do método, etc. declarados noproto
no URI (por exemplo,/path/a6.EchoService/Echo
), usar correspondência absoluta impedirá que o plugin extraia informações doproto
do URI.$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri":"/*", // modo de correspondência de prefixo "plugins":{ "grpc-web":{} // habilite o plugin gRPC Web }, "upstream":{ "scheme":"grpc", "type":"roundrobin", "nodes":{ "127.0.0.1:50001":1 // endereços e portas de escuta do gRPC Server } } }'
-
Valide as requisições de proxy gRPC Web.
A requisição do protocolo gRPC Web pode ser enviada ao Apache APISIX executando
client.js
a partir do Node.A lógica de processamento do lado do cliente e do servidor acima é, respectivamente: o cliente envia uma mensagem ao servidor com o conteúdo
hello
, o servidor recebe a mensagem e responde comresponse: hello
, e o resultado da execução é o seguinte.$ node client.js response: hello
-
Desabilite o plugin de proxy
grpc-web
.Basta remover o atributo grpc-web da configuração do plugin de roteamento.
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri":"/*", "plugins":{ }, "upstream":{ "scheme":"grpc", "type":"roundrobin", "nodes":{ "127.0.0.1:50001":1 } } }'
Resumo
Este artigo traz uma experiência prática sobre o uso do grpc-web
no Apache APISIX.
Sinta-se à vontade para iniciar uma discussão no GitHub Discussions ou se comunicar via lista de e-mails.