Как заставить работать балансировку нагрузки GRPC round_robin с использованием grpc-java в кластере Kubernetes?

Я пытался заставить балансировку нагрузки GRPC работать в моем приложении Java, развернутом в кластере Kubernetes, но у меня не было особого успеха. Кажется, что по этому поводу не слишком много документации, но из примеров в Интернете я вижу, что теперь я могу использовать '.defaultLoadBalancingPolicy (round_robin)' при настройке ManagedChannel (в более поздних версиях GRPC Java lib).

Чтобы быть более конкретным, я использую версию 1.34.1 библиотек Java GRPC. Я создал два приложения Spring Boot (v2.3.4), одно называется grpc-sender, а второе grpc-Receiver.

grpc-sender действует как клиент GRPC и определяет (Netty) ManagedChannel как:

@Bean
public ManagedChannel greetingServiceManagedChannel() {
  String host = "grpc-receiver";
  int port = 6565;
  return NettyChannelBuilder.forAddress(host, port)
      .defaultLoadBalancingPolicy("round_robin")
      .usePlaintext().build();
}

Тогда grpc-Receiver действует как сервер GRPC:

Server server = ServerBuilder.forPort(6565)
        .addService(new GreetingServiceImpl()).build();

Я развертываю эти приложения в кластере Kubernetes (пока работает локально в minikube), и я создал сервис для приложения grpc-Receiver как безголовый сервис:

kind: Service
apiVersion: v1
metadata:
  name: grpc-receiver
spec:
  clusterIP: None
  selector:
    app: grpc-receiver
  ports:
    - name: 'grpc'
      port: 6565
      protocol: 'TCP'
      targetPort: 6565

Однако, когда я пытаюсь отправить сообщение от grpc-sender к grpc-Receiver, я просто вижу это исключение в журналах grpc-sender:

2021-01-08 17:46:24.494 ERROR 1 --- [ault-executor-0] io.grpc.internal.ManagedChannelImpl      : [Channel<1>: (grpc-receiver:6565)] Uncaught exception in the SynchronizationContext. Panic!
java.lang.NoSuchFieldError: NAME_RESOLVER_SERVICE_CONFIG
    at io.grpc.services.HealthCheckingLoadBalancerFactory$HealthCheckingLoadBalancer.handleResolvedAddresses(HealthCheckingLoadBalancerFactory.java:186) ~[grpc-services-1.25.0.jar!/:1.25.0]
    at io.grpc.internal.AutoConfiguredLoadBalancerFactory$AutoConfiguredLoadBalancer.tryHandleResolvedAddresses(AutoConfiguredLoadBalancerFactory.java:154) ~[grpc-core-1.34.1.jar!/:1.34.1]
    at io.grpc.internal.ManagedChannelImpl$NameResolverListener$1NamesResolved.run(ManagedChannelImpl.java:1668) ~[grpc-core-1.34.1.jar!/:1.34.1]
    at io.grpc.SynchronizationContext.drain(SynchronizationContext.java:95) ~[grpc-api-1.34.1.jar!/:1.34.1]
    at io.grpc.SynchronizationContext.execute(SynchronizationContext.java:127) ~[grpc-api-1.34.1.jar!/:1.34.1]
    at io.grpc.internal.ManagedChannelImpl$NameResolverListener.onResult(ManagedChannelImpl.java:1682) ~[grpc-core-1.34.1.jar!/:1.34.1]
    at io.grpc.internal.DnsNameResolver$Resolve.run(DnsNameResolver.java:333) ~[grpc-core-1.34.1.jar!/:1.34.1]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na

Может ли кто-нибудь подсказать мне, что я делаю неправильно или чего мне не хватает?

Кстати, мне известны альтернативные подходы к балансировке нагрузки с помощью GRPC, такие как Service Mesh, например Linkerd или Istio, или просто использование прокси-сервера Envoy, но я очень хочу, чтобы что-то работало с использованием встроенных функций балансировки нагрузки GRPC, таких как точка сравнения между различными подходами.

Спасибо большое!


person Daniel Western    schedule 08.01.2021    source источник
comment
примечание: политика grpclb гораздо более надежна, и ее следует использовать вместо нее, если это возможно.   -  person morgwai    schedule 13.05.2021


Ответы (1)


Я обнаружил некоторую проблему в вашем коде:

  1. В клиенте вы должны использовать forTarget, а не forAddress, например dns:///grpc-receiver:6565, потому что kubernetes service route by dns, попробуйте этот преобразователь имен и убедитесь, что ваш сервер имеет несколько экземпляров.
  2. Исключение NoSuchFieldError: NAME_RESOLVER_SERVICE_CONFIG вызвано тем, что lib не совпадает, версия grpc-services - 1.25.0, а версия grpc-core - 1.34.1, поэтому сделайте их в той же версии, и она должна работать правильно.
person HelloWood    schedule 10.01.2021
comment
Большое спасибо @HelloWood! Второй пункт действительно решил проблему, более старая версия grpc-services вводилась устаревшей версией другой зависимости, io.github.lognet: grpc-spring-boot-starter. Вскоре после обновления появилась версия 1.34.1 grpc-services, и циклическая балансировка нагрузки работала отлично. Кстати, он все еще работает, даже если forAddress используется только с именем службы Kubernetes как «grpc-Receiver», а не forTarget, как предлагается. - person Daniel Western; 13.01.2021