grpc v1.34.1 的客户端负载平衡,nameResolverFactory 已弃用

Posted

技术标签:

【中文标题】grpc v1.34.1 的客户端负载平衡,nameResolverFactory 已弃用【英文标题】:Clientside load balancing for grpc v1.34.1, nameResolverFactory is deprecated 【发布时间】:2020-12-23 14:13:16 【问题描述】:

我正在使用带有 Java 的 grpc v1.34.1,并且很难配置客户端负载平衡,因为此版本中不推荐使用某些方法。在早期版本中配置客户端负载平衡非常简单:

final ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
        .nameResolverFactory(new DnsNameResolverProvider())  // this is on by default
        .loadBalancerFactory(RoundRobinLoadBalancerFactory.getInstance())
        .usePlaintext(true)
        .build();

或者通过这个https://sultanov.dev/blog/grpc-client-side-load-balancing/

但是,对于已弃用 nameResolverFactory 并删除方法 loadBalancerFactory 的较新版本,没有任何可用参考。

NameResolver.Factory nameResolverFactory = new MultiAddressNameResolverFactory(
        new InetSocketAddress("localhost", 50000),
        new InetSocketAddress("localhost", 50001),
        new InetSocketAddress("localhost", 50002)
);

channel = ManagedChannelBuilder.forTarget("localhost")
        .nameResolverFactory(nameResolverFactory)
        .defaultLoadBalancingPolicy("round_robin")
        .usePlaintext()
        .build();

客户端负载平衡有效。但是,较新的 API 已弃用 nameResolverFactory

谁能指出我在较新版本中使用nameResolverFactory 替代不同服务器(主机和端口)的客户端负载平衡?

【问题讨论】:

defaultLoadBalancingPolicy() 是正确的方法。我看不出有什么明显的错误。您的目标字符串被误解的可能性很小;您可以尝试在其前面加上 dns:/// 以保证使用 DnsNameResolver。我们不希望“localhost”有任何真正的负载平衡,所以我假设您通常使用target。您如何验证负载平衡是否正常工作? 我有一个调用 NodeJs 服务器的 java 客户端。由于 NodeJs 是单线程的,我有多个使用 pm2 运行它的实例。 NodeJS 正在执行 CPU 密集型任务。因此,可以很容易地注意到其中一个过程中的尖峰。但是,所有其他服务器都处于空闲状态,利用率为 0%。 作为一种解决方法,我创建了多个通道和存根,并随机选择其中一个连接到服务器。这提供了更高的机会命中服务器的副本(不同的实例) 如果您的多个通道/存根代码使用相同的目标字符串,那么这意味着 DNS 设置正确,可以为您的目标返回多个条目。在这种情况下,您的 defaultLoadBalancingPolicy 也应该可以工作。 @San P 不幸的是,它没有用。我在构造函数中创建了通道对象并重用它以避免使用targetdefaultLoadBalancingPolicy 重新创建通道,因为它的成本很高。但是,我为每个调用创建一个新的存根。 【参考方案1】:

经过grpc-java内部实现后,我发现新版本接受NameResolver.Factory对象的方式略有不同。封装到NameResolverProvider,需要注册到默认NameResolverRegistry。下面分享了在较新版本中执行此操作的示例代码:

NameResolverProvider nameResolverFactory = new MultiAddressNameResolverFactory(
                new InetSocketAddress("localhost", 50000),
                new InetSocketAddress("localhost", 50001),
                new InetSocketAddress("localhost", 50002)
        );

NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
nameResolverRegistry.register(nameResolverFactory);
channel = ManagedChannelBuilder.forTarget("localhost")
          .defaultLoadBalancingPolicy("round_robin")
          .usePlaintext()
          .build();


public class MultiAddressNameResolverFactory extends NameResolverProvider 
    final List<EquivalentAddressGroup> addresses;

    MultiAddressNameResolverFactory(SocketAddress... addresses) 
        this.addresses = Arrays.stream(addresses)
                .map(EquivalentAddressGroup::new)
                .collect(Collectors.toList());
    

    public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) 
        return new NameResolver() 
            @Override
            public String getServiceAuthority() 
                return "fakeAuthority";
            
            public void start(Listener2 listener) 
                listener.onResult(ResolutionResult.newBuilder().setAddresses(addresses).setAttributes(Attributes.EMPTY).build());
            
            public void shutdown() 
            
        ;
    

    @Override
    public String getDefaultScheme() 
        return "multiaddress";
    

    @Override
    protected boolean isAvailable() 
        return true;
    

    @Override
    protected int priority() 
        return 0;
    

默认情况下,您的 NameResolver.Factory 自定义实现将被通道拾取以连接到服务器。根据负载均衡策略,会选择一个SocketAddress 连接到服务器。

【讨论】:

以上是关于grpc v1.34.1 的客户端负载平衡,nameResolverFactory 已弃用的主要内容,如果未能解决你的问题,请参考以下文章

在服务器端输出上禁用 gRPC 负载平衡

gRPC 客户端负载均衡

07平衡负载:gRPC是如何进行负载均衡的?

GKE 上的 GRPC 负载平衡(在 L7、HTTP/2 + TLS 上)

使用envoy在k8s中作grpc的负载均衡

.NET gRPC核心功能初体验