I am using grpc v1.34.1 with Java and it's hard to configure client-side load balancing since some of the methods are deprecated in this version. It was pretty straightforward to configure client-side load balancing in an earlier version by:
final ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
.nameResolverFactory(new DnsNameResolverProvider()) // this is on by default
.loadBalancerFactory(RoundRobinLoadBalancerFactory.getInstance())
.usePlaintext(true)
.build();
Or by this https://sultanov.dev/blog/grpc-client-side-load-balancing/
But, there aren't any references available for a newer version that has deprecated nameResolverFactory
and removed method 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();
Client-side load balancing works. But, the newer API has deprecated nameResolverFactory
.
Could anyone please point me towards the alternative of nameResolverFactory
in the newer version for client-side load balancing with different servers (hosts and ports)?
After going through grpc-java internal implementation, I found that that the newer version accepts the NameResolver.Factory
object in slightly different way. It's encapsulated to NameResolverProvider
which is required to be registered to default NameResolverRegistry
. Sample code to do this in newer version is shared below:
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;
}
}
By default, your custom implementation for NameResolver.Factory would be picked up by channel to connect to server. Based on the load balancing policy, a SocketAddress
would be picked up to connect to server.