Integración de Apache APISIX con gRPC-Web

Fei Han

January 25, 2022

Ecosystem

Introducción a gRPC Web

Originalmente desarrollado por Google, gRPC es un marco de llamada a procedimiento remoto de alto rendimiento implementado sobre HTTP/2. Sin embargo, dado que los navegadores no exponen directamente HTTP/2, las aplicaciones web no pueden usar gRPC directamente. gRPC Web es un protocolo estandarizado que resuelve este problema.

La primera implementación de gRPC-web se lanzó en 2018 como una biblioteca de JavaScript a través de la cual las aplicaciones web pueden comunicarse directamente con el servicio gRPC. El principio es crear una canalización gRPC de extremo a extremo compatible con HTTP/1.1 y HTTP/2. Luego, el navegador envía una solicitud HTTP regular, y un proxy gRPC-Web ubicado entre el navegador y el servidor traduce la solicitud y la respuesta. Similar a gRPC, gRPC Web utiliza un contrato predefinido entre el cliente web y el servicio gRPC back-end. Se utilizan Protocol Buffers para serializar y codificar los mensajes.

Cómo funciona gRPC-web

Con gRPC Web, los usuarios pueden llamar a aplicaciones gRPC back-end directamente utilizando un navegador o un cliente Node. Sin embargo, existen algunas limitaciones al usar gRPC-Web en el lado del navegador para llamar a servicios gRPC.

  • No se admiten llamadas de transmisión unidireccional y bidireccional en el lado del cliente.
  • Llamar a servicios gRPC a través de dominios requiere que se configure CORS en el lado del servidor.
  • El lado del servidor gRPC debe estar configurado para admitir gRPC-Web, o debe estar disponible un servicio proxy de terceros para traducir la llamada entre el navegador y el servidor.

Proxy gRPC Web de Apache APISIX

Apache APISIX admite el proxy del protocolo gRPC Web mediante un complemento, que completa el trabajo de conversión de protocolo y codificación de datos cuando gRPC Web se comunica con gRPC Server en el complemento grpc-web, y su proceso de comunicación es el siguiente.

Cliente gRPC Web -> Apache APISIX (conversión de protocolo y codificación de datos) -> Servidor gRPC

A continuación se muestra un ejemplo completo que muestra cómo construir un Cliente gRPC Web y cómo proxy las solicitudes gRPC Web a través de Apache APISIX. En el siguiente ejemplo, usaremos Go como el manejador del servidor gRPC Server y Node como el solicitante del cliente gRPC Web.

Configurar Protocol Buffer

El primer paso es instalar el compilador de Protocol Buffer y los complementos relacionados.

  1. Instalar protoc y proto-grpc-*.

    El compilador de Protocol Buffer protoc y los complementos protoc-gen-go y protoc-gen-grpc-web para generar código de interfaz Go, JavaScript y gRPC web para .proto deben estar instalados en su sistema antes de escribir aplicaciones del lado del cliente y del servidor.

    Ejecute el siguiente script para instalar los componentes mencionados.

    #!/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
    
  2. Crear el archivo proto de ejemplo SayHello.

    // 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 la aplicación del servidor

  1. Generar mensajes brutos de Go y stubs de servicio/cliente en el servidor.

    protoc -I./a6 echo.proto --go_out=plugins=grpc:./a6
    
  2. Implementar la interfaz del manejador del 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
    }
    
  3. Archivo de entrada de la aplicación del 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)
    }
    }
    
  4. Compilar e iniciar el servicio del servidor.

    go build -o grpc-server server.go
    ./grpc-server
    

Configurar programas del cliente

  1. Generar código proto del cliente.

    Generar mensajes brutos de JavaScript, stubs de servicio/cliente y código de interfaz para el JavaScript de gRPC Web.

    El complemento proto para gRPC Web proporciona dos modos de generación de código.

    1. mode=grpcwebtext: El código generado por defecto envía la carga útil en formato grpc-web-text.

    • Content-type: application/grpc-web-text

    • La carga útil utiliza codificación base64

    • Admite llamadas unarias y de transmisión del servidor

    1. mode=grpcweb: enviar la carga útil en formato binario protobuf.

    • Content-type: application/grpc-web+proto
    • La carga útil está en formato binario protobuf
    • Actualmente solo se admiten llamadas unarias
    protoc -I=./a6 echo.proto --js_out=import_style=commonjs:./a6 --grpc-web_out=import_style=commonjs,mode=grpcweb:./a6
    
  2. Instalar dependencias del cliente.

    npm i grpc-web
    npm i google-protobuf
    
  3. Ejecutar archivo de entrada en el cliente.

    // client.js
    const {EchoRequest} = require('./a6/echo_pb');
    const {EchoServiceClient} = require('./a6/echo_grpc_web_pb');
    // conectar a la entrada de 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());
        }
    });
    
  4. Estructura final del proyecto

    $ 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
    

Después de completar los pasos anteriores, habrá configurado la aplicación del servidor gRPC Server y la aplicación del cliente gRPC Web, y habrá iniciado la aplicación del servidor, que recibirá solicitudes en el puerto 50001.

Configurar Apache APISIX

A continuación, simplemente habilite el complemento grpc-web en la configuración del complemento de enrutamiento de Apache APISIX para proxy las solicitudes gRPC web.

  1. Habilitar el complemento proxy grpc-web.

    El enrutamiento debe usar coincidencia de prefijo (por ejemplo, /* o /grpc/example/*), porque el cliente gRPC web pasa el nombre del paquete, el nombre de la interfaz de servicio, el nombre del método, etc. declarados en proto en el URI (por ejemplo, /path/a6.EchoService/Echo), usar coincidencia absoluta evitará que el complemento extraiga la información de proto del URI.

    $ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
        "uri":"/*", // modo de coincidencia de prefijo
        "plugins":{
            "grpc-web":{} // habilitar complemento gRPC Web
        },
        "upstream":{
            "scheme":"grpc",
            "type":"roundrobin",
            "nodes":{
                "127.0.0.1:50001":1 // direcciones y puertos de escucha del servidor gRPC
            }
        }
    }'
    
  2. Validar solicitudes de proxy gRPC Web.

    La solicitud del protocolo gRPC Web se puede enviar a Apache APISIX ejecutando client.js desde Node.

    La lógica de procesamiento del cliente y del servidor anterior es respectivamente: el cliente envía un mensaje al servidor con el contenido hello, el servidor recibe el mensaje y responde con response: hello, y el resultado de la ejecución es el siguiente.

    $ node client.js
    response: hello
    
  3. Deshabilitar el complemento proxy grpc-web.

    Simplemente elimine el atributo grpc-web de la configuración del complemento de enrutamiento.

    $ 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
            }
        }
    }'
    

Resumen

Este artículo le brinda una experiencia práctica sobre el uso de grpc-web en Apache APISIX.

No dude en iniciar una discusión en GitHub Discussions o comunicarse a través de la lista de correo.

Tags: