Integration von Apache APISIX mit gRPC-Web

Fei Han

January 25, 2022

Ecosystem

gRPC Web Einführung

Ursprünglich von Google entwickelt, ist gRPC ein hochperformantes Remote Procedure Call Framework, das auf HTTP/2 implementiert ist. Da Browser jedoch HTTP/2 nicht direkt verfügbar machen, können Webanwendungen gRPC nicht direkt verwenden. gRPC Web ist ein standardisiertes Protokoll, das dieses Problem löst.

Die erste gRPC-Web-Implementierung wurde 2018 als JavaScript-Bibliothek veröffentlicht, über die Webanwendungen direkt mit dem gRPC-Dienst kommunizieren können. Das Prinzip besteht darin, eine end-to-end gRPC-Pipeline zu erstellen, die mit HTTP/1.1 und HTTP/2 kompatibel ist. Der Browser sendet dann eine reguläre HTTP-Anfrage, und ein gRPC-Web-Proxy, der sich zwischen dem Browser und dem Server befindet, übersetzt die Anfrage und die Antwort. Ähnlich wie gRPC verwendet gRPC Web einen vordefinierten Vertrag zwischen dem Webclient und dem Backend-gRPC-Dienst. Protocol Buffers werden verwendet, um Nachrichten zu serialisieren und zu kodieren.

Wie gRPC-web funktioniert

Mit gRPC Web können Benutzer Backend-gRPC-Anwendungen direkt über einen Browser oder einen Node-Client aufrufen. Es gibt jedoch einige Einschränkungen bei der Verwendung von gRPC-Web auf der Browserseite, um gRPC-Dienste aufzurufen.

  • Client-seitiges Streaming und bidirektionale Streaming-Aufrufe werden nicht unterstützt.
  • Der Aufruf von gRPC-Diensten über Domänen hinweg erfordert, dass CORS auf der Serverseite konfiguriert wird.
  • Die gRPC-Serverseite muss so konfiguriert sein, dass sie gRPC-Web unterstützt, oder ein Drittanbieter-Dienstagent muss verfügbar sein, um den Aufruf zwischen dem Browser und dem Server zu übersetzen.

Apache APISIX gRPC Web Proxy

Apache APISIX unterstützt das Proxying des gRPC-Web-Protokolls durch ein Plugin, das im grpc-web-Plugin die Protokollumwandlung und Datenkodierung übernimmt, wenn gRPC Web mit dem gRPC-Server kommuniziert. Der Kommunikationsprozess ist wie folgt.

gRPC Web Client -> Apache APISIX (Protokollumwandlung & Datenkodierung) -> gRPC-Server

Das folgende Beispiel zeigt, wie man einen gRPC Web Client erstellt und gRPC-Web-Anfragen über Apache APISIX proxied. In diesem Beispiel verwenden wir Go als gRPC-Server-Handler und Node als gRPC-Web-Client-Anforderer.

Protocol Buffer konfigurieren

Der erste Schritt besteht darin, den Protocol Buffer-Compiler und die zugehörigen Plugins zu installieren.

  1. Installieren Sie protoc und proto-grpc-*.

    Der Protocol Buffer-Compiler protoc und die Plugins protoc-gen-go und protoc-gen-grpc-web zur Generierung von Go-, JavaScript- und gRPC-Web-Interface-Code für .proto müssen auf Ihrem System installiert sein, bevor Sie Client- und Serveranwendungen schreiben.

    Führen Sie das folgende Skript aus, um die oben genannten Komponenten zu installieren.

    #!/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. Erstellen Sie die SayHello-Beispiel-proto-Datei.

    // 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);
    }
    

Serveranwendung konfigurieren

  1. Generieren Sie serverseitige Go-Rohdaten und Service/Client-Stubs.

    protoc -I./a6 echo.proto --go_out=plugins=grpc:./a6
    
  2. Implementieren Sie die serverseitige Handler-Schnittstelle.

    // 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. Die serverseitige Anwendungslaufzeit-Eingabedatei.

    // 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. Kompilieren und starten Sie den serverseitigen Dienst.

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

Client-Programme konfigurieren

  1. Generieren Sie clientseitigen proto-Code.

    Generieren Sie clientseitige JavaScript-Rohdaten, Service/Client-Stubs und Interface-Code für gRPC Web's JavaScript.

    Das proto-Plugin für gRPC Web bietet zwei Modi der Codegenerierung.

    1. mode=grpcwebtext: Der standardmäßig generierte Code sendet die Nutzdaten im grpc-web-text-Format.

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

    • Nutzdaten verwenden Base64-Kodierung

    • Unterstützt monadische und Server-Streaming-Aufrufe

    1. mode=grpcweb: sendet Nutzdaten im binären Protobuf-Format.

    • Content-type: application/grpc-web+proto
    • Nutzdaten sind im binären Protobuf-Format
    • Derzeit werden nur monadische Aufrufe unterstützt
    protoc -I=./a6 echo.proto --js_out=import_style=commonjs:./a6 --grpc-web_out=import_style=commonjs,mode=grpcweb:./a6
    
  2. Installieren Sie clientseitige Abhängigkeiten.

    npm i grpc-web
    npm i google-protobuf
    
  3. Führen Sie die Eingabedatei auf der Clientseite aus.

    // client.js
    const {EchoRequest} = require('./a6/echo_pb');
    const {EchoServiceClient} = require('./a6/echo_grpc_web_pb');
    // Verbindung zum Eingang von 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. Endgültige Projektstruktur

    $ 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
    

Nachdem Sie die obigen Schritte abgeschlossen haben, haben Sie die gRPC-Server-Serveranwendung und die gRPC-Web-Clientanwendung konfiguriert und die Serveranwendung gestartet, die Anfragen auf Port 50001 empfängt.

Apache APISIX konfigurieren

Als Nächstes aktivieren Sie einfach das grpc-web-Plugin in der Apache APISIX-Routing-Plugin-Konfiguration, um gRPC-Web-Anfragen zu proxen.

  1. Aktivieren Sie das grpc-web-Proxy-Plugin.

    Das Routing muss Präfix-Matching verwenden (z.B. /* oder /grpc/example/*), da der gRPC-Web-Client den Paketnamen, den Service-Interface-Namen, den Methodennamen usw., die in proto deklariert sind, im URI übergibt (z.B. /path/a6.EchoService/Echo). Die Verwendung von absolutem Matching verhindert, dass das Plugin proto-Informationen aus dem URI extrahiert.

    $ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
        "uri":"/*", // Präfix-Matching-Modus
        "plugins":{
            "grpc-web":{} // gRPC Web Plugin aktivieren
        },
        "upstream":{
            "scheme":"grpc",
            "type":"roundrobin",
            "nodes":{
                "127.0.0.1:50001":1 // gRPC Server Listen-Adressen und Ports
            }
        }
    }'
    
  2. Validieren Sie gRPC-Web-Proxy-Anfragen.

    Die gRPC-Web-Protokollanfrage kann an Apache APISIX gesendet werden, indem client.js von Node ausgeführt wird.

    Die obige Client- und Server-Verarbeitungslogik ist wie folgt: Der Client sendet eine Nachricht mit dem Inhalt hello an den Server, der Server empfängt die Nachricht und antwortet mit response: hello. Das Ausführungsergebnis ist wie folgt.

    $ node client.js
    response: hello
    
  3. Deaktivieren Sie das grpc-web-Proxy-Plugin.

    Entfernen Sie einfach das grpc-web-Attribut aus der Routing-Plugin-Konfiguration.

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

Zusammenfassung

Dieser Artikel bietet Ihnen praktische Erfahrungen mit der Verwendung von grpc-web in Apache APISIX.

Fühlen Sie sich frei, eine Diskussion in GitHub Discussions zu starten oder über die Mailingliste zu kommunizieren.

Tags: