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 不幸的是,它没有用。我在构造函数中创建了通道对象并重用它以避免使用target
和defaultLoadBalancingPolicy
重新创建通道,因为它的成本很高。但是,我为每个调用创建一个新的存根。
【参考方案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 已弃用的主要内容,如果未能解决你的问题,请参考以下文章