Интеграция Apache APISIX с gRPC-Web

Fei Han

January 25, 2022

Ecosystem

Введение в gRPC Web

Изначально разработанный Google, gRPC — это высокопроизводительный фреймворк для удалённого вызова процедур, реализованный на основе HTTP/2. Однако, поскольку браузеры не предоставляют прямой доступ к HTTP/2, веб-приложения не могут использовать gRPC напрямую. gRPC Web — это стандартизированный протокол, который решает эту проблему.

Первая реализация gRPC-web была выпущена в 2018 году в виде библиотеки JavaScript, через которую веб-приложения могут напрямую взаимодействовать с gRPC-сервисом. Принцип работы заключается в создании сквозного gRPC-конвейера, совместимого с HTTP/1.1 и HTTP/2. Браузер отправляет обычный HTTP-запрос, а gRPC-Web прокси, расположенный между браузером и сервером, преобразует запрос и ответ. Подобно gRPC, gRPC Web использует предопределённый контракт между веб-клиентом и серверной gRPC-службой. Для сериализации и кодирования сообщений используются Protocol Buffers.

Как работает gRPC-web

С помощью gRPC Web пользователи могут напрямую вызывать серверные gRPC-приложения через браузер или Node-клиент. Однако при использовании gRPC-Web на стороне браузера для вызова gRPC-сервисов существуют некоторые ограничения.

  • Не поддерживаются потоковые вызовы на стороне клиента и двунаправленные потоковые вызовы.
  • Для вызова gRPC-сервисов через домены необходимо настроить CORS на стороне сервера.
  • Серверная часть gRPC должна быть настроена для поддержки gRPC-Web, или должен быть доступен сторонний сервисный агент для преобразования вызова между браузером и сервером.

Прокси gRPC Web в Apache APISIX

Apache APISIX поддерживает проксирование протокола gRPC Web с помощью плагина, который выполняет преобразование протокола и кодирование данных при взаимодействии gRPC Web с gRPC Server в плагине grpc-web. Процесс взаимодействия выглядит следующим образом.

gRPC Web Client -> Apache APISIX (преобразование протокола и кодирование данных) -> gRPC сервер

Ниже приведён полный пример, демонстрирующий, как создать gRPC Web Client и проксировать запросы gRPC Web через Apache APISIX. В этом примере мы будем использовать Go в качестве обработчика серверной части gRPC Server и Node в качестве клиента gRPC Web.

Настройка Protocol Buffer

Первым шагом является установка компилятора Protocol Buffer и связанных плагинов.

  1. Установите protoc и proto-grpc-*.

    Компилятор Protocol Buffer protoc и плагины protoc-gen-go и protoc-gen-grpc-web для генерации кода интерфейсов Go, JavaScript и gRPC Web для .proto должны быть установлены на вашей системе перед написанием клиентских и серверных приложений.

    Запустите следующий скрипт для установки вышеуказанных компонентов.

    #!/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. Создайте пример proto файла 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); }

Настройка серверного приложения

  1. Сгенерируйте серверные Go-сообщения и заглушки сервиса/клиента.

    protoc -I./a6 echo.proto --go_out=plugins=grpc:./a6
  2. Реализуйте интерфейс обработчика на стороне сервера.

    // 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. Точка входа для запуска серверного приложения.

    // 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. Скомпилируйте и запустите серверную службу.

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

Настройка клиентских программ

  1. Сгенерируйте клиентский proto код.

    Сгенерируйте клиентские JavaScript-сообщения, заглушки сервиса/клиента и код интерфейса для JavaScript gRPC Web.

    Плагин proto для gRPC Web предоставляет два режима генерации кода.

    1. mode=grpcwebtext: По умолчанию генерируется код, который отправляет данные в формате grpc-web-text.

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

    • Данные кодируются в base64

    • Поддерживает монолитные и серверные потоковые вызовы

    1. mode=grpcweb: отправляет данные в бинарном формате protobuf.

    • Content-type: application/grpc-web+proto
    • Данные в бинарном формате protobuf
    • В настоящее время поддерживаются только монолитные вызовы
    protoc -I=./a6 echo.proto --js_out=import_style=commonjs:./a6 --grpc-web_out=import_style=commonjs,mode=grpcweb:./a6
  2. Установите клиентские зависимости.

    npm i grpc-web npm i google-protobuf
  3. Точка входа для выполнения на стороне клиента.

    // client.js const {EchoRequest} = require('./a6/echo_pb'); const {EchoServiceClient} = require('./a6/echo_grpc_web_pb'); // подключение к входу 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. Окончательная структура проекта

    $ 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

После выполнения вышеуказанных шагов вы настроили серверное приложение gRPC Server и клиентское приложение gRPC Web, а также запустили серверное приложение, которое будет принимать запросы на порту 50001.

Настройка Apache APISIX

Далее просто включите плагин grpc-web в конфигурации плагинов маршрутизации Apache APISIX для проксирования запросов gRPC Web.

  1. Включите плагин прокси grpc-web.

    Маршрутизация должна использовать префиксное сопоставление (например, /* или /grpc/example/*), так как gRPC Web клиент передаёт имя пакета, имя сервисного интерфейса, имя метода и т.д., объявленные в proto, в URI (например, /path/a6.EchoService/Echo). Использование абсолютного сопоставления предотвратит извлечение информации proto из URI плагином.

    $ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri":"/*", // режим префиксного сопоставления "plugins":{ "grpc-web":{} // включить плагин gRPC Web }, "upstream":{ "scheme":"grpc", "type":"roundrobin", "nodes":{ "127.0.0.1:50001":1 // адреса и порты прослушивания gRPC Server } } }'
  2. Проверка запросов прокси gRPC Web.

    Запрос по протоколу gRPC Web можно отправить в Apache APISIX, выполнив client.js из Node.

    Логика обработки на стороне клиента и сервера соответственно: клиент отправляет сообщение на сервер с содержимым hello, сервер получает сообщение и отвечает response: hello. Результат выполнения следующий.

    $ node client.js response: hello
  3. Отключите плагин прокси grpc-web.

    Просто удалите атрибут grpc-web из конфигурации плагинов маршрутизации.

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

Заключение

Эта статья предоставляет вам практический опыт использования grpc-web в Apache APISIX.

Не стесняйтесь начать обсуждение в GitHub Discussions или связаться через рассылку.

Tags: