Envoy, gRPC и ограничение скорости

задняя часть Go C++ gRPC Service Mesh
авторVenil Noronha| ПереводчикВан Цюаньген| РецензентЯн Чуаньшэн Ван Кай| 2500 слов | Около 5 минут на чтение

EnvoyЭто облегченный сервисный прокси, разработанный для приложений Cloud Native, а также один из немногих поддерживаемыхgRPCодин из агентов. gRPC — этоHTTP/2высокопроизводительный RPC (удаленный вызов процедур) framework, который поддерживает несколько языков.

В этом посте мы будем использовать gRPC иProtocol BuffersСборка языковой версии C++Приветственное приложение,использоватьGoязык для создания другого приложения gRPC, которое реализует возможности Envoy.RateLimitServiceинтерфейс. Наконец, разверните Envoy в качестве прокси для приложения Greeter, реализованного с помощью нашего сервиса ограничения скорости.механизм противодавления(обратное давление).

Приложение приветствия gRPC

Сначала мы устанавливаемgRPCа такжеProtobuf,ПотомСоздайте приложение Greeter на языке C++.. Вы также можете выбратьДругие языки, указанные в документациидля создания этого приложения, однако в этой статье я буду использовать C++.

Ниже представлена ​​схема приложения Greeter.

При запуске приложения Greeter в терминале появляется следующий вывод:

$ ./greeter_serverServer listening on 0.0.0.0:50051
$ ./greeter_clientGreeter received: Hello world

Обновление приложения gRPC Greeter

Теперь мы улучшили приложение Greeter, заменив статический префикс «Hello» возвращаемым значением с префиксом количества запросов. просто обновиgreeter_server.ccфайл, как показано ниже.

 // Logic and data behind the server's behavior. class GreeterServiceImpl final : public Greeter::Service {+  int counter = 0;   Status SayHello(ServerContext* context, const HelloRequest* request,                   HelloReply* reply) override {-    std::string prefix("Hello ");+    std::string prefix(std::to_string(++counter) + " ");     reply->set_message(prefix + request->name());     return Status::OK;   }

затем пересобрать и запуститьgreeter_server,пройти черезgreeter_clientВы можете увидеть следующий вывод при отправке запроса.

$ for i in {1..3}; do ./greeter_client; sleep 1; doneGreeter received: 1 worldGreeter received: 2 worldGreeter received: 3 world

Простой сервис ограничения скорости

Затем мы расширяем EnvoyRateLimitServiceПрототип интерфейса для реализации простой службы ограничения скорости в Go. Для этого мы создаемrate-limit-serviceПроект Go и введение Envoygo-control-planeи другие сопутствующие зависимости.go-control-planeПроект предоставляет привязки языка Go для прототипа Envoy. Для того, чтобы позже реализовать сервис ограничения скорости, нам также необходимо создатьcmd/server/main.goа такжеcmd/client/main.goдва файла.

$ mkdir -p $GOPATH/src/github.com/venilnoronha/rate-limit-service/$ cd $GOPATH/src/github.com/venilnoronha/rate-limit-service/$ mkdir -p cmd/server/ && touch cmd/server/main.go$ mkdir cmd/client/ && touch cmd/client/main.go

После импорта всех зависимостей у вас будет структура проекта, как показано ниже. Обратите внимание, что я выделил только те пакеты, которые относятся к этому эксперименту.

── rate-limit-service   ├── cmd   │   ├── client   │   │   └── main.go   │   └── server   │       └── main.go   └── vendor       ├── github.com       │   ├── envoyproxy       │   │   ├── data-plane-api       │   │   └── go-control-plane       │   ├── gogo       │   │   ├── googleapis       │   │   └── protobuf       │   └── lyft       │       └── protoc-gen-validate       └── google.golang.org           ├── genproto           └── grpc

сервер ограничения скорости

Теперь давайте создадим простую службу ограничения скорости gRPC, чтобы ограничить количество запросов в секунду.

package main​import (    "log"    "net"    "golang.org/x/net/context"    "google.golang.org/grpc"    "google.golang.org/grpc/reflection"    rls "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2")​// server is used to implement rls.RateLimitServicetype server struct{    // limit specifies if the next request is to be rate limited    limit bool}​func (s *server) ShouldRateLimit(ctx context.Context,        request *rls.RateLimitRequest) (*rls.RateLimitResponse, error) {    log.Printf("request: %v\n", request)​    // logic to rate limit every second request    var overallCode rls.RateLimitResponse_Code    if s.limit {        overallCode = rls.RateLimitResponse_OVER_LIMIT        s.limit = false    } else {        overallCode = rls.RateLimitResponse_OK        s.limit = true    }​    response := &rls.RateLimitResponse{OverallCode: overallCode}    log.Printf("response: %v\n", response)        return response, nil}​func main() {    // create a TCP listener on port 50052        lis, err := net.Listen("tcp", ":50052")        if err != nil {                log.Fatalf("failed to listen: %v", err)        }    log.Printf("listening on %s", lis.Addr())​    // create a gRPC server and register the RateLimitService server        s := grpc.NewServer()    rls.RegisterRateLimitServiceServer(s, &server{limit: false})        reflection.Register(s)        if err := s.Serve(lis); err != nil {                log.Fatalf("failed to serve: %v", err)        }}

запускатьRateLimitServiceПосле подачи вывод терминала выглядит следующим образом.

$ go run cmd/server/main.go2018/10/27 00:35:28 listening on [::]:50052

клиент ограничения скорости

Мы также создаемRateLimitServiceКлиент для проверки поведения сервера.

package main​import (        "log"    "time"        "golang.org/x/net/context"        "google.golang.org/grpc"    rls "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2")​func main() {        // Set up a connection to the server        conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())        if err != nil {                log.Fatalf("could not connect: %v", err)        }        defer conn.Close()        c := rls.NewRateLimitServiceClient(conn)​        // Send a request to the server        ctx, cancel := context.WithTimeout(context.Background(), time.Second)        defer cancel()    r, err := c.ShouldRateLimit(ctx, &rls.RateLimitRequest{Domain: "envoy"})        if err != nil {                log.Fatalf("could not call service: %v", err)        }        log.Printf("response: %v", r)}

Теперь давайте проверим взаимодействие сервера и клиента, запустив клиент.

$ for i in {1..4}; do go run cmd/client/main.go; sleep 1; done2018/10/27 17:32:23 response: overall_code:OK2018/10/27 17:32:25 response: overall_code:OVER_LIMIT2018/10/27 17:32:26 response: overall_code:OK2018/10/27 17:32:28 response: overall_code:OVER_LIMIT

Журналы, связанные с сервером.

2018/10/27 17:32:23 request: domain:"envoy"2018/10/27 17:32:23 response: overall_code:OK2018/10/27 17:32:25 request: domain:"envoy"2018/10/27 17:32:25 response: overall_code:OVER_LIMIT2018/10/27 17:32:26 request: domain:"envoy"2018/10/27 17:32:26 response: overall_code:OK2018/10/27 17:32:28 request: domain:"envoy"2018/10/27 17:32:28 response: overall_code:OVER_LIMIT

Прокси посланника

Теперь мы представляем прокси-сервер Envoy, который направляет запросы от клиента Greeter на сервер Greeter, одновременно проверяя скорость с помощью нашего сервиса ограничения скорости. На диаграмме ниже показана наша окончательная структура развертывания.

конфигурация прокси

Мы используем следующую конфигурацию Envoy для регистрации сервисов Greeter и RateLimitService и включения проверки ограничения скорости. Обратите внимание, что поскольку мы развертываем Envoy вDocker for MacВкл., локально развернутые службыdocker.for.mac.localhostуказан адрес.

static_resources:  listeners:  - address:      socket_address:        address: 0.0.0.0        port_value: 9211 # expose proxy on port 9211    filter_chains:    - filters:      - name: envoy.http_connection_manager        config:          codec_type: auto          stat_prefix: ingress_http          access_log: # configure logging            name: envoy.file_access_log            config:              path: /dev/stdout          route_config:            name: greeter_route # configure the greeter service routes            virtual_hosts:            - name: service              domains:              - "*"              routes:              - match:                  prefix: "/"                  grpc: {}                route:                  cluster: greeter_service              rate_limits: # enable rate limit checks for the greeter service                actions:                - destination_cluster: {}          http_filters:          - name: envoy.rate_limit # enable the Rate Limit filter            config:              domain: envoy          - name: envoy.router # enable the Router filter            config: {}  clusters:  - name: greeter_service # register the Greeter server    connect_timeout: 1s    type: strict_dns    lb_policy: round_robin    http2_protocol_options: {} # enable H2 protocol    hosts:    - socket_address:        address: docker.for.mac.localhost        port_value: 50051  - name: rate_limit_service # register the RateLimitService server    connect_timeout: 1s    type: strict_dns    lb_policy: round_robin    http2_protocol_options: {} # enable H2 protocol    hosts:    - socket_address:        address: docker.for.mac.localhost        port_value: 50052rate_limit_service: # define the global rate limit service  use_data_plane_proto: true  grpc_service:    envoy_grpc:      cluster_name: rate_limit_service

Разверните прокси-сервер Envoy

Чтобы развернуть прокси-сервер Envoy, мы копируем приведенную выше конфигурацию вenvoy.yamlдокумент. Затем мы используем следующееDockerfileСоздайте образ Docker.

FROM envoyproxy/envoy:latestCOPY envoy.yaml /etc/envoy/envoy.yaml

Создайте образ с помощью следующей команды:

$ docker build -t envoy:grpc .Sending build context to Docker daemon  74.75kBStep 1/2 : FROM envoyproxy/envoy:latest ---> 51fc619e4dc5Step 2/2 : COPY envoy.yaml /etc/envoy/envoy.yaml ---> c766ba3d7d09Successfully built c766ba3d7d09Successfully tagged envoy:grpc

Затем запустите агент:

$ docker run -p 9211:9211 envoy:grpc...[2018-10-28 02:59:20.469][000008][info][main] [source/server/server.cc:456] starting main dispatch loop[2018-10-28 02:59:20.553][000008][info][upstream] [source/common/upstream/cluster_manager_impl.cc:135] cm init: all clusters initialized[2018-10-28 02:59:20.554][000008][info][main] [source/server/server.cc:425] all clusters initialized. initializing init manager[2018-10-28 02:59:20.554][000008][info][config] [source/server/listener_manager_impl.cc:908] all dependencies initialized. starting workers

Обновите клиент Greeter

Поскольку мы хотим использовать Envoy для маршрутизации запросов от клиентов Greeter, мы меняем порт сервера в клиентском коде с50051изменить на9211, и восстановить.

   GreeterClient greeter(grpc::CreateChannel(-      "localhost:50051", grpc::InsecureChannelCredentials()));+      "localhost:9211", grpc::InsecureChannelCredentials()));   std::string user("world");   std::string reply = greeter.SayHello(user);

финальный тест

На данный момент у нас есть сервер Greeter, служба RateLimitService и прокси-сервер Envoy, и пришло время проверить все развертывание. Для этого мы используем обновленный клиент Greeter для отправки нескольких запросов, как показано ниже.

$ for i in {1..10}; do ./greeter_client; sleep 1; doneGreeter received: 4 world14:Greeter received: RPC failedGreeter received: 5 world14:Greeter received: RPC failedGreeter received: 6 world14:Greeter received: RPC failedGreeter received: 7 world14:Greeter received: RPC failedGreeter received: 8 world14:Greeter received: RPC failed

Как видите, 5 из 10 запросов были успешными, чередуясь с кодом состояния gRPC14изRPC failedНеудачный запрос. Это указывает на то, что служба ограничения скорости намеренно регулирует запросы, а Envoy корректно завершает последующие запросы.

В заключение

Эта статья даст вам общее представление о том, как использовать Envoy в качестве прокси-сервера приложения, а также поможет вам понять, как фильтры ограничения скорости Envoy работают с протоколом gRPC.