Intégration d'Apache APISIX avec gRPC-Web

Fei Han

January 25, 2022

Ecosystem

Introduction à gRPC Web

Développé à l'origine par Google, gRPC est un framework d'appel de procédure distante haute performance implémenté sur HTTP/2. Cependant, comme les navigateurs n'exposent pas directement HTTP/2, les applications Web ne peuvent pas utiliser gRPC directement. gRPC Web est un protocole standardisé qui résout ce problème.

La première implémentation de gRPC-web a été publiée en 2018 sous la forme d'une bibliothèque JavaScript grâce à laquelle les applications Web peuvent communiquer directement avec le service gRPC. Le principe est de créer un pipeline gRPC de bout en bout compatible avec HTTP/1.1 et HTTP/2. Le navigateur envoie ensuite une requête HTTP régulière, et un proxy gRPC-Web situé entre le navigateur et le serveur traduit la requête et la réponse. Similaire à gRPC, gRPC Web utilise un contrat prédéfini entre le client Web et le service gRPC back-end. Les protocoles Buffers sont utilisés pour sérialiser et encoder les messages.

fonctionnement de gRPC-web

Avec gRPC Web, les utilisateurs peuvent appeler des applications gRPC back-end directement en utilisant un navigateur ou un client Node. Cependant, il existe certaines limitations lors de l'utilisation de gRPC-Web côté navigateur pour appeler des services gRPC.

  • Les appels de streaming côté client et les appels de streaming bidirectionnels ne sont pas pris en charge.
  • L'appel de services gRPC entre domaines nécessite que CORS soit configuré côté serveur.
  • Le côté serveur gRPC doit être configuré pour prendre en charge gRPC-Web, ou un service proxy tiers doit être disponible pour traduire l'appel entre le navigateur et le serveur.

Proxy gRPC Web d'Apache APISIX

Apache APISIX prend en charge le proxy du protocole gRPC Web au moyen d'un plugin, qui effectue la conversion de protocole et le codage des données lorsque gRPC Web communique avec gRPC Server dans le plugin grpc-web, et son processus de communication est le suivant.

Client gRPC Web -> Apache APISIX (conversion de protocole & codage des données) -> Serveur gRPC

Voici un exemple complet montrant comment construire un client gRPC Web et proxyer les requêtes gRPC Web via Apache APISIX. Dans l'exemple suivant, nous utiliserons Go comme gestionnaire de serveur gRPC Server et Node comme demandeur de client gRPC Web.

Configuration de Protocol Buffer

La première étape consiste à installer le compilateur Protocol Buffer et les plugins associés.

  1. Installer protoc et proto-grpc-*.

    Le compilateur Protocol Buffer protoc et les plugins protoc-gen-go et protoc-gen-grpc-web pour générer le code d'interface Go, JavaScript et gRPC Web pour .proto doivent être installés sur votre système avant d'écrire les applications client et serveur.

    Veuillez exécuter le script suivant pour installer les composants ci-dessus.

    #!/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. Créer le fichier proto d'exemple 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);
    }
    

Configuration de l'application serveur

  1. Générer les messages bruts Go côté serveur et les stubs de service/client.

    protoc -I./a6 echo.proto --go_out=plugins=grpc:./a6
    
  2. Implémenter l'interface de gestionnaire côté serveur.

    // 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. Fichier d'entrée d'exécution de l'application serveur.

    // 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. Compiler et démarrer le service serveur.

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

Configuration des programmes clients

  1. Générer le code proto côté client.

    Générer les messages bruts JavaScript côté client, les stubs de service/client et le code d'interface pour le JavaScript de gRPC Web.

    Le plugin proto pour gRPC Web fournit deux modes de génération de code.

    1. mode=grpcwebtext: Le code généré par défaut envoie la charge utile au format grpc-web-text.

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

    • La charge utile utilise l'encodage base64

    • Prend en charge les appels monadiques et de streaming serveur

    1. mode=grpcweb: envoyer la charge utile au format binaire protobuf.

    • Content-type: application/grpc-web+proto
    • La charge utile est au format binaire protobuf
    • Seuls les appels monadiques sont actuellement pris en charge
    protoc -I=./a6 echo.proto --js_out=import_style=commonjs:./a6 --grpc-web_out=import_style=commonjs,mode=grpcweb:./a6
    
  2. Installer les dépendances côté client.

    npm i grpc-web
    npm i google-protobuf
    
  3. Fichier d'entrée d'exécution côté client.

    // client.js
    const {EchoRequest} = require('./a6/echo_pb');
    const {EchoServiceClient} = require('./a6/echo_grpc_web_pb');
    // se connecter à l'entrée d'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. Structure finale du projet

    $ 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
    

Après avoir terminé les étapes ci-dessus, vous avez configuré l'application serveur gRPC Server et l'application client gRPC Web, et démarré l'application serveur, qui recevra les requêtes sur le port 50001.

Configuration d'Apache APISIX

Ensuite, il suffit d'activer le plugin grpc-web dans la configuration du plugin de routage d'Apache APISIX pour proxyer les requêtes gRPC Web.

  1. Activer le plugin proxy grpc-web.

    Le routage doit utiliser la correspondance de préfixe (par exemple, /* ou /grpc/example/*), car le client gRPC Web passe le nom du package, le nom de l'interface de service, le nom de la méthode, etc. déclarés dans proto dans l'URI (par exemple, /path/a6.EchoService/Echo), l'utilisation de la correspondance absolue empêchera le plugin d'extraire les informations proto de l'URI.

    $ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
        "uri":"/*", // mode de correspondance de préfixe
        "plugins":{
            "grpc-web":{} // activer le plugin gRPC Web
        },
        "upstream":{
            "scheme":"grpc",
            "type":"roundrobin",
            "nodes":{
                "127.0.0.1:50001":1 // adresses et ports d'écoute du serveur gRPC
            }
        }
    }'
    
  2. Valider les requêtes proxy gRPC Web.

    La requête de protocole gRPC Web peut être envoyée à Apache APISIX en exécutant client.js depuis Node.

    Les logiques de traitement côté client et serveur ci-dessus sont respectivement : le client envoie un message au serveur avec le contenu hello, le serveur reçoit le message et répond avec response: hello, et le résultat de l'exécution est le suivant.

    $ node client.js
    response: hello
    
  3. Désactiver le plugin proxy grpc-web.

    Il suffit de supprimer l'attribut grpc-web de la configuration du plugin de routage.

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

Résumé

Cet article vous apporte une expérience pratique sur l'utilisation de grpc-web dans Apache APISIX.

N'hésitez pas à démarrer une discussion dans GitHub Discussions ou à communiquer via la liste de diffusion.

Tags: