【问题标题】:How to get GRPC's round_robin load balancing to work using grpc-java in Kubernetes cluster?如何让 GRPC 的 round_robin 负载平衡在 Kubernetes 集群中使用 grpc-java 工作?
【发布时间】:2023-04-06 10:09:01
【问题描述】:

我一直在尝试让 GRPC 的负载平衡在部署到 Kubernetes 集群的 Java 应用程序中工作,但我没有取得太大的成功。似乎没有太多关于此的文档,但从在线示例中我可以看到,我现在应该能够在设置 ManagedChannel 时使用 '.defaultLoadBalancingPolicy("round_robin")'(在 GRPC Java lib 的更高版本中) .

更具体地说,我使用的是 1.34.1 版的 GRPC Java 库。我创建了两个 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 进行负载平衡的替代方法,例如使用 Linkerd 或 Istio 等服务网格或仅使用 Envoy 代理,但我渴望使用 GRPC 的开箱即用负载来工作平衡特征作为不同方法之间的比较点。

非常感谢!

【问题讨论】:

  • 注意:策略grpclb 更加健壮,如果可能,应该使用它。

标签: java kubernetes load-balancing grpc grpc-java


【解决方案1】:

我发现你的代码有问题:

  1. 在客户端,你应该使用forTarget而不是forAddress,比如dns:///grpc-receiver:6565,因为kubernetes服务路由通过dns,试试这个名称解析器,并确保你的服务器有多个实例。
  2. 异常NoSuchFieldError: NAME_RESOLVER_SERVICE_CONFIG是lib不匹配引起的,grpc-services版本是1.25.0,而grpc-core版本是1.34.1,所以要在同一个版本,应该可以正常使用。

【讨论】:

  • 非常感谢@HelloWood!第二点确实解决了问题,旧版本的 grpc-services 是由另一个依赖项 io.github.lognet:grpc-spring-boot-starter 的过时版本引入的。更新后,它引入了 grpc-services 的 1.34.1 版本,并且循环负载平衡完美运行。顺便说一句,即使 forAddress 仅使用 Kubernetes 服务的名称作为“grpc-receiver”,它仍然有效,而不是建议的 forTarget。
猜你喜欢
  • 2021-04-18
  • 2018-11-22
  • 2019-10-20
  • 2017-09-30
  • 2019-02-02
  • 1970-01-01
  • 2022-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多