Служба балансировки нагрузки gRPC с помощью Nginx

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

Крупномасштабное развертывание gRPC обычно включает несколько идентичных внутренних серверов и несколько клиентов. Балансировка нагрузки используется для оптимального распределения нагрузки от клиентов по доступным серверам.

Виды балансировки нагрузки

Существует два основных варианта балансировки нагрузки gRPC: на стороне сервера и на стороне клиента. Решение о том, какой из них использовать, является основным архитектурным выбором.

Балансировка нагрузки на стороне сервера

При балансировке нагрузки на стороне сервера клиент отправляет RPC балансировщику нагрузки или прокси-серверу, например Nginx или Envoy. Балансировщик нагрузки распределяет вызов RPC на один из доступных внутренних серверов.

Он также отслеживает нагрузку на каждый сервер и реализует алгоритмы справедливого распределения нагрузки. Сами клиенты не знают о внутренних серверах.

Альтернативный текст

Балансировка нагрузки на стороне клиента

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

Альтернативный текст

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

Альтернативный текст

Другой подход — использовать внешний балансировщик нагрузки, в котором функции балансировки нагрузки реализованы на специальном сервере балансировки нагрузки. Клиенты запрашивают резервный балансировщик нагрузки, чтобы выбрать лучший сервер(ы) для использования. Тяжелая работа по сохранению состояния сервера, обнаружению сервисов и реализации алгоритма балансировки нагрузки объединена в резервном балансировщике нагрузки.

За и против

Одним из плюсов балансировки нагрузки на стороне сервера является простая реализация клиента. Все, что нужно знать клиенту, — это адрес прокси, никакого дополнительного кодирования не требуется. Этот подход работает даже для ненадежных клиентов, а это означает, что служба gRPC может быть открыта для всех из общедоступного Интернета.

Однако его минусом является добавление к вызову еще 1 дополнительного перехода. Все RPC должны пройти через прокси-сервер, прежде чем достичь внутреннего сервера, что приводит к более высокой задержке. Таким образом, такая балансировка нагрузки на стороне сервера подходит для случаев, когда имеется много клиентов из открытого Интернета, возможно, не заслуживающих доверия, которые хотят подключиться к нашим серверам gRPC в центре обработки данных.

Альтернативный текст

С другой стороны, балансировка нагрузки на стороне клиента не добавляет к вызову дополнительных переходов и, таким образом, обеспечивает более высокую производительность в целом. Однако реализация клиента теперь становится сложной, особенно для подхода с толстым клиентом. Поэтому его следует использовать только для доверенных клиентов, иначе нам придется использовать внешний балансировщик нагрузки, чтобы стоять перед сетью границы доверия. Балансировка нагрузки на стороне клиента часто используется в системах с очень высоким трафиком и архитектуре микросервисов.

В этой статье мы узнаем, как настроить балансировку нагрузки на стороне сервера для наших gRPC сервисов с помощью Nginx.

Рефакторинг кода

Поскольку я собираюсь показать вам различные Nginxконфигурации, в которых TLS можно включать и отключать на сервере и клиенте, давайте немного обновим наш код, чтобы включить новый аргумент командной строки.

Обновление сервера

На сервере давайте добавим новый логический флаг enableTLS, который сообщит нам, хотим ли мы включить TLS на нашем сервере gRPC или нет. Его значение по умолчанию — false.

func main() {
    port := flag.Int("port", 0, "the server port")
    enableTLS := flag.Bool("tls", false, "enable SSL/TLS")

    flag.Parse()
    log.Printf("start server on port %d, TLS = %t", *port, *enableTLS)

    ...
}

Затем давайте вынесем перехватчики в отдельную serverOptions переменную. Проверяем enableTLS флаг. Только в случае, если он включен, мы загружаем учетные данные TLS и добавляем эти учетные данные в срез параметров сервера. Наконец, мы просто передаем параметры сервера вызову grpc.NewServer() функции.

func main() {
    ...

    interceptor := service.NewAuthInterceptor(jwtManager, accessibleRoles())
    serverOptions := []grpc.ServerOption{
        grpc.UnaryInterceptor(interceptor.Unary()),
        grpc.StreamInterceptor(interceptor.Stream()),
    }

    if *enableTLS {
        tlsCredentials, err := loadTLSCredentials()
        if err != nil {
            log.Fatal("cannot load TLS credentials: ", err)
        }

        serverOptions = append(serverOptions, grpc.Creds(tlsCredentials))
    }

    grpcServer := grpc.NewServer(serverOptions...)

    ...
}

И это все для сервера. Давайте сделаем то же самое для клиента!

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

Сначала мы добавляем enableTLS флаг в аргумент командной строки. Затем мы определяем transportOption переменную со значением по умолчанию grpc.WithInsecure().

func main() {
    serverAddress := flag.String("address", "", "the server address")
    enableTLS := flag.Bool("tls", false, "enable SSL/TLS")

    flag.Parse()
    log.Printf("dial server %s, TLS = %t", *serverAddress, *enableTLS)

    transportOption := grpc.WithInsecure()

    if *enableTLS {
        tlsCredentials, err := loadTLSCredentials()
        if err != nil {
            log.Fatal("cannot load TLS credentials: ", err)
        }

        transportOption = grpc.WithTransportCredentials(tlsCredentials)
    }

    cc1, err := grpc.Dial(*serverAddress, transportOption)
    if err != nil {
        log.Fatal("cannot dial server: ", err)
    }

    authClient := client.NewAuthClient(cc1, username, password)
    interceptor, err := client.NewAuthInterceptor(authClient, authMethods(), refreshDuration)
    if err != nil {
        log.Fatal("cannot create auth interceptor: ", err)
    }

    cc2, err := grpc.Dial(
        *serverAddress,
        transportOption,
        grpc.WithUnaryInterceptor(interceptor.Unary()),
        grpc.WithStreamInterceptor(interceptor.Stream()),
    )
    if err != nil {
        log.Fatal("cannot dial server: ", err)
    }

    laptopClient := client.NewLaptopClient(cc2)
    testRateLaptop(laptopClient)
}

Только когда enableTLS значение флага равно true, мы загружаем учетные данные TLS из файлов PEM и меняем их transportOption на grpc.WithTransportCredentials(tlsCredentials). Наконец, мы передаем transportOption соединениям grpc. И клиент готов.

Протестируйте новый флаг

Теперь, если мы запустим make server, мы увидим, что сервер работает с отключенным TLS.

Альтернативный текст

А если мы запустим make client, он также будет работать без TLS, и все вызовы RPC будут успешными.

Альтернативный текст

Если мы добавим -tlsфлаг к make serverкоманде и перезапустим ее, TLS будет включен.

...

server:
    go run cmd/server/main.go -port 8080 -tls

...

Альтернативный текст

Если мы запустим make client сейчас, запросы не будут выполнены:

Альтернативный текст

Нам также необходимо включить TLS на стороне клиента, добавив -tls флаг в make client команду.

...

client:
    go run cmd/client/main.go -address 0.0.0.0:8080 -tls

...

Альтернативный текст

И теперь мы видим, что запросы снова успешны.

Обновить Makefile

Хорошо, теперь флаг TLS работает так, как мы хотели. Прежде чем добавлять Nginx, давайте Makefile немного обновим наш код, чтобы мы могли легко запускать несколько экземпляров сервера и клиента с TLS или без него.

Я удалю -tls флаги, чтобы команды make server и make clientвыполнялись без TLS. И я добавлю еще 2 команды make для запуска 2 экземпляров сервера на разных портах. Допустим, первый сервер будет работать на порту 50051, а второй сервер будет работать на порту 50052.

...

server:
    go run cmd/server/main.go -port 8080

client:
    go run cmd/client/main.go -address 0.0.0.0:8080

server1:
    go run cmd/server/main.go -port 50051

server2:
    go run cmd/server/main.go -port 50052

...

Давайте также добавим еще 3 команды make для запуска клиента и серверов с помощью TLS. Команда client-tls запустит клиент с TLS. Команда make server1-tls запустит сервер TLS на порту 50051, а затем make server2-tls команда запустит другой сервер TLS на порту 50052.

...

client-tls:
    go run cmd/client/main.go -address 0.0.0.0:8080 -tls

server1-tls:
    go run cmd/server/main.go -port 50051 -tls

server2-tls:
    go run cmd/server/main.go -port 50052 -tls

...

Установка Nginx

Следующее, что нам нужно сделать, это установить Nginx. Поскольку мы используем Mac, мы можем просто использовать Homebrew:

❯ brew install nginx

После установки nginx мы можем перейти в эту usr/local/etc/nginx папку, чтобы настроить его. Давайте откроем nginx.conf файл с кодом Visual Studio.

❯ cd /usr/local/etc/nginx
❯ code nginx.conf

Это конфигурация по умолчанию:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    include servers/*;
}

Есть несколько вещей, о которых нам не нужно беспокоиться в этом уроке, поэтому давайте обновим этот файл конфигурации.

Конфигурация Nginx для небезопасного gRPC

Во-первых, давайте удалим пользовательскую конфигурацию, раскомментируем журнал ошибок, удалим конфигурацию для уровней журнала и идентификатора процесса, и скажем, на данный момент нам нужно всего лишь 10 рабочих подключений.

Одна важная вещь, которую нам нужно сделать, — это настроить правильное место для хранения журнала ошибок и доступа к файлам журнала. В моем случае Homebrew уже создал папку журнала для Nginx at /usr/local/var/log/nginx, поэтому я просто использую ее в настройках error/access log.

worker_processes  1;

error_log  /usr/local/var/log/nginx/error.log;

events {
    worker_connections  10;
}

http {
    access_log  /usr/local/var/log/nginx/access.log;

    server {
        listen       8080 http2;

        location / {
        }
    }
}

Теперь в блоке сервера у нас есть listen команда для прослушивания входящих запросов от клиента на порту 8080. Это конфигурация по умолчанию для обычного HTTP-сервера. Поскольку gRPC использует HTTP/2, нам следует добавить http2 в конце этой команды.

Давайте удалим имя сервера и кодировку, поскольку они нам сейчас не нужны. То же самое касается и журнала доступа, поскольку мы уже определили его выше. Давайте также удалим конфигурацию корневого HTML-файла по умолчанию и все, что находится после блока, location поскольку они нам пока не нужны.

Хорошо, теперь мы хотим сбалансировать нагрузку входящих запросов на наши два экземпляра сервера. Поэтому мы должны определить upstream для них. Я позвоню upstream pcbook_services.

Внутри этого блока мы используем server ключевое слово для объявления экземпляра сервера. Первый работает на localhost порту 50051, а второй работает на порту 50052.

worker_processes  1;

error_log  /usr/local/var/log/nginx/error.log;

events {
    worker_connections  10;
}

http {
    access_log  /usr/local/var/log/nginx/access.log;

    upstream pcbook_services {
        server 0.0.0.0:50051;
        server 0.0.0.0:50052;
    }

    server {
        listen       8080 http2;

        location / {
            grpc_pass grpc://pcbook_services;
        }
    }
}

Затем, чтобы направить все вызовы RPC в восходящий поток, в location блоке мы используем grpc_pass ключевое слово, за которым следует grpc:// схема и имя восходящего потока, то есть pcbook_services.

Вот и все! Балансировка нагрузки для нашего незащищенного сервера gRPC завершена.

Давайте запустим nginx в терминале, чтобы запустить его.

❯ nginx
❯ ps aux | grep nginx
quangpham         9013   0.0  0.0  4408572    800 s000  S+    6:13PM   0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox nginx
quangpham         9007   0.0  0.0  4562704   1124   ??  S     6:12PM   0:00.00 nginx: worker process
quangpham         9006   0.0  0.0  4422416    612   ??  Ss    6:12PM   0:00.00 nginx: master process nginx

Мы можем проверить, запущен он или нет, используя команду ps and grep. Давайте проверим папку журнала:

❯ cd /usr/local/var/log/nginx
❯ ls -l
total 0
-rw-r--r--  1 quangpham  admin  0 Oct 11 18:12 access.log
-rw-r--r--  1 quangpham  admin  0 Oct 11 18:12 error.log

Как видите, создаются 2 файла журнала: access.log и error.log. На данный момент они пусты, поскольку мы еще не отправляли никаких запросов.

Теперь побежим make server1 запускать первый сервер на порту 50051с TLS = false. Затем на другой вкладке запустите make server2 второй сервер на порту 50052, также с отключенным TLS. Наконец, мы запускаем make client еще одну новую вкладку.

Альтернативный текст

Выглядит неплохо. Все вызовы RPC успешны. Давайте проверим логи на наших серверах.

Альтернативный текст

Получает server2 2 запроса на создание ноутбука.

Альтернативный текст

И server1 получает 1 запрос на вход и 1 запрос на создание ноутбука. Отличный!

И через некоторое время на этот сервер поступает еще один запрос на вход. Это потому, что наш клиент все еще работает и периодически вызывает вход в систему для обновления токена.

Альтернативный текст

Хорошо, теперь давайте посмотрим на файл access log nginx.

Альтернативный текст

Вы можете видеть, что сначала происходит входной вызов, затем 3 создания вызовов для ноутбука и, наконец, еще один входной вызов. Итак, все работает именно так, как мы ожидаем.

Далее я покажу вам, как SSL/TLS включить Nginx.

Конфигурация Nginx для gRPC с TLS

В типичном развертывании серверы gRPC уже работают внутри доверенной сети, и только балансировщик нагрузки ( Nginxв данном случае) доступен общедоступному Интернету. Таким образом, мы можем оставить наши серверы gRPC работающими без них TLS, как и раньше, и добавлять TLS только Nginx.

Включите TLS на Nginx, но держите серверы gRPC небезопасными

Для этого нам нужно скопировать 3 файла pem в папку конфигурации nginx:

  • Сертификат сервера
  • Закрытый ключ сервера
  • И сертификат центра сертификации, подписавшего сертификат клиента, если мы используем взаимный TLS.

Хорошо, теперь я перейду cd к /usr/local/etc/nginxпапке и создам новую cert папку. Затем я скопирую эти 3 файла pem из нашего pcbook проекта в эту папку.

❯ cd /usr/local/etc/nginx
❯ mkdir cert
❯ cp ~/Projects/techschool/pcbook-go/cert/server-cert.pem cert
❯ cp ~/Projects/techschool/pcbook-go/cert/server-key.pem cert
❯ cp ~/Projects/techschool/pcbook-go/cert/ca-cert.pem cert

Хорошо, теперь все файлы сертификатов и ключей готовы. Давайте вернемся к нашему конфигурационному файлу nginx.

Чтобы включить TLS, нам сначала нужно добавить ssl в listen команду. Затем мы используем ssl_certificate команду, чтобы указать Nginxместоположение файла сертификата сервера. И используйте ssl_certificate_key команду, чтобы указать расположение файла закрытого ключа сервера.

...

    server {
        listen       8080 ssl http2;

        ssl_certificate cert/server-cert.pem;
        ssl_certificate_key cert/server-key.pem;

        ssl_client_certificate cert/ca-cert.pem;
        ssl_verify_client on;

        location / {
            grpc_pass grpc://pcbook_services;
        }
    }

...

Поскольку мы используем взаимный TLS, нам также необходимо использовать ssl_client_certificate команду, чтобы сообщить nginx расположение файла сертификата ЦС клиента. И, наконец, мы указываем ssl_verify_client nginx on проверить подлинность сертификата, который отправит клиент.

И мы закончили. Перезапустим nginx. Мы бежим nginx -s stop, чтобы остановить его первыми. Затем мы запускаем его с помощью nginx команды.

❯ nginx -s stop
❯ nginx

Наш сервер уже запущен, давайте запустим клиент!

Если мы просто запустим make client, он будет работать без TLS, поэтому запрос завершится неудачно, поскольку Nginx теперь он выполняется с включенным TLS.

Альтернативный текст

Теперь вызовите make client-tls.

Альтернативный текст

На этот раз клиент работает с TLS, и все запросы выполняются успешно.

Имейте в виду, что наши серверы по-прежнему работают без TLS. Nginx Итак, в основном происходит следующее: безопасно только соединение между клиентом и клиентом , а Nginx к нашим внутренним серверам подключается через другое небезопасное соединение.

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

Хорошо, но что, если они не находятся в одной доверенной сети? Что ж, в этом случае у нас нет другого выбора, кроме как включить TLS на наших внутренних серверах и настроить nginx для работы с ним.

Включите TLS на серверах Nginx и gRPC.

Давайте остановим текущие server1 и server2, а затем перезапустим их с помощью TLS.

❯ make server1-tls
❯ make server2-tls

Теперь, если мы запустим make client-tls немедленно, запрос завершится неудачно.

Альтернативный текст

Причина в том, что, хотя рукопожатие TLS между клиентом и Nginx успешным, рукопожатие TLS между Nginx нашими внутренними серверами не удалось, поскольку внутренние серверы теперь ожидают безопасного соединения TLS, но Nginx все еще используют небезопасное соединение при подключении к внутренним серверам.

Альтернативный текст

Как вы можете видеть в журнале ошибок, сбой произошел при Nginx разговоре с вышестоящими серверами.

Чтобы включить безопасное соединение TLS между nginx и вышестоящим сервером, в nginx.conf файле нам необходимо изменить grpc схему на grpcs.

...

    server {
        ...

        location / {
            grpc_pass grpcs://pcbook_services;
        }
    }

...

Этого должно быть достаточно, если мы просто используем TLS на стороне сервера. Однако в этом случае мы используем взаимный TLS, поэтому, если мы просто перезапустим Nginx сейчас и повторно запустим make client-tls запрос, он все равно завершится неудачей, поскольку Nginx он еще не настроен для отправки своего сертификата вышестоящим серверам.

Альтернативный текст

У нас есть bad certificate ошибка, как вы можете видеть в журнале.

Давайте посмотрим, что произойдет, если мы перейдем к коду сервера cmd/server/main.go и изменим ClientAuth поле с tls.RequireAndVerifyClientCert на tls.NoClientCert, что означает, что мы будем просто использовать TLS на стороне сервера.

func loadTLSCredentials() (credentials.TransportCredentials, error) {
    ...

    // Create the credentials and return it
    config := &tls.Config{
        Certificates: []tls.Certificate{serverCert},
        ClientAuth:   tls.NoClientCert,
        ClientCAs:    certPool,
    }

    return credentials.NewTLS(config), nil
}

Затем перезапустите server1-tls и снова server2-tls запустите make client-tls.

❯ make server1-tls
❯ make server2-tls
❯ make client-tls

Альтернативный текст

На этот раз все запросы успешны. Это именно то, что мы ожидали!

Хорошо, а что, если нам действительно нужен взаимный TLS между nginx и восходящим потоком?

Давайте ClientAuth снова изменим поле на tls.RequireAndVerifyClientCert, перезапустим два внутренних сервера TLS и вернемся к нашему nginx.conf файлу.

На этот раз мы должны дать указание Nginxвыполнить взаимный TLS с внутренними серверами, указав расположение сертификата и закрытого ключа. Мы используем grpc_ssl_certificateключевое слово для сертификата и grpc_ssl_certificate_key ключевое слово для закрытого ключа.

Nginx Если хотите, вы можете создать другую пару сертификата и закрытого ключа . Здесь я просто использую тот же сертификат и закрытый ключ серверов.

worker_processes  1;

error_log  /usr/local/var/log/nginx/error.log;

events {
    worker_connections  10;
}

http {
    access_log  /usr/local/var/log/nginx/access.log;

    upstream pcbook_services {
        server 0.0.0.0:50051;
        server 0.0.0.0:50052;
    }

    server {
        listen       8080 ssl http2;

        # Mutual TLS between gRPC client and nginx
        ssl_certificate cert/server-cert.pem;
        ssl_certificate_key cert/server-key.pem;

        ssl_client_certificate cert/ca-cert.pem;
        ssl_verify_client on;

        location / {
            grpc_pass grpcs://pcbook_services;

            # Mutual TLS between nginx and gRPC server
            grpc_ssl_certificate cert/server-cert.pem;
            grpc_ssl_certificate_key cert/server-key.pem;
        }
    }
}

ОК, давайте попробуем.

Сначала остановите текущий процесс nginx, затем запустите новый. И make client-tls снова запустите.

❯ nginx -s stop
❯ nginx
❯ make client-tls

Альтернативный текст

На этот раз все запросы успешны.

Несколько мест маршрутизации

Прежде чем мы закончим, я хочу показать вам еще одну вещь.

Как вы уже видели, запросы на вход в систему и создание ноутбука теперь равномерно распределены между нашими двумя внутренними серверами. Но иногда нам может потребоваться разделить службу аутентификации и службу бизнес-логики.

Например, предположим, что мы хотим, чтобы все запросы на вход направлялись на сервер 1, а все остальные запросы — на сервер 2. В этом случае мы также можем указать Nginx маршрутизацию запросов на основе их пути.

worker_processes  1;

error_log  /usr/local/var/log/nginx/error.log;

events {
    worker_connections  10;
}

http {
    access_log  /usr/local/var/log/nginx/access.log;

    upstream auth_services {
        server 0.0.0.0:50051;
        server 0.0.0.0:50052;
    }

    upstream laptop_services {
        server 0.0.0.0:50051;
        server 0.0.0.0:50052;
    }

    server {
        listen       8080 ssl http2;

        # Mutual TLS between gRPC client and nginx
        ssl_certificate cert/server-cert.pem;
        ssl_certificate_key cert/server-key.pem;

        ssl_client_certificate cert/ca-cert.pem;
        ssl_verify_client on;

        location /techschool.pcbook.AuthService {
            grpc_pass grpcs://auth_services;

            # Mutual TLS between nginx and gRPC server
            grpc_ssl_certificate cert/server-cert.pem;
            grpc_ssl_certificate_key cert/server-key.pem;
        }

        location /techschool.pcbook.LaptopService {
            grpc_pass grpcs://laptop_services;

            # Mutual TLS between nginx and gRPC server
            grpc_ssl_certificate cert/server-cert.pem;
            grpc_ssl_certificate_key cert/server-key.pem;
        }
    }
}

Здесь мы просто копируем путь /techschool.pcbook.AuthService и AuthService вставляем его в это место. Затем мы меняем это имя восходящего потока на auth_services. Он должен подключаться только к server1 порту at 50051.

Затем мы добавляем еще один восходящий поток laptop_services и заставляем его подключаться только server2 к порту 50052. Затем продублируйте блок местоположения, измените имя восходящего потока на laptop_services и обновите путь на techschool.pcbook.LaptopService.

Хорошо, давайте попробуем это! Нам просто нужно перезагрузить компьютер Nginx и запустить make client-tls.

Альтернативный текст

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

Альтернативный текст

А все остальные запросы на создание ноутбука отправляются в server2. Даже если мы запустим это, make client-tls несколько раз.

Так что это работает! На этом наша лекция о балансировке нагрузки gRPC с помощью Nginx подошла к концу.