Reputation: 1187
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)?
Upvotes: 5
Views: 2911
Reputation: 150
just one thing to add to Nitish Bhardwaj answer, for me method
int priority()
did not work with value 0, because io.grpc.internal.DnsNameResolverProvider has the value of 5 and io.grpc.netty.shaded.io.grpc.netty.UdsNameResolverProvider has the value of 3, which in my case was not setting my custom NameResolverProvider, I could fix it to be automatically picking up my custom NameResolverProvider by overriding method priority()
to anything more than 5.
full code:
package iam.mfa.grpc.client.resolvers;
import java.net.URI;
import java.util.List;
import io.grpc.Attributes;
import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import lombok.RequiredArgsConstructor;
/**
* @author HAMMA FATAKA ([email protected])
* @project gRPC
* @date 07.04.2023 21:54
*/
@RequiredArgsConstructor(staticName = "of")
public class MultipleAddressNameResolverProvider extends NameResolverProvider {
private final List<EquivalentAddressGroup> addresses;
@Override
public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
return new NameResolver() {
@Override
public String getServiceAuthority() {
return "noAuthority";
}
@Override
public void start(Listener2 listener) {
listener.onResult(ResolutionResult.newBuilder().setAddresses(addresses).setAttributes(Attributes.EMPTY).build());
}
@Override
public void shutdown() {
}
};
}
@Override
public String getDefaultScheme() {
return "multiTarget";
}
@Override
protected boolean isAvailable() {
return true;
}
@Override
protected int priority() {
return 6;
}
}
Client config:
package iam.mfa.grpc.client.config;
import java.net.InetSocketAddress;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import iam.mfa.grpc.client.resolvers.MultipleAddressNameResolver;
import io.grpc.EquivalentAddressGroup;
import io.grpc.ManagedChannel;
import io.grpc.NameResolverRegistry;
import io.grpc.internal.DnsNameResolver;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
/**
* @author HAMMA FATAKA ([email protected])
* @project gRPC
* @date 26.02.2023 16:03
*/
@Configuration
public class ClientConfig {
@Value("#{'${grpc.client.targets}'.split(',')}")
private List<String> targets;
@PostConstruct
public void initNameResolver(){
final var addresses = targets.stream()
.map(target -> {
final var splitTarget = target.split(":");
final var host = splitTarget[0];
final var port = splitTarget[1];
return new EquivalentAddressGroup(new InetSocketAddress(host, Integer.parseInt(port)));
})
.toList();
final var nameResolverFactory = MultipleAddressNameResolver.of(addresses);
NameResolverRegistry.getDefaultRegistry().register(nameResolverFactory);
}
@Bean
public ManagedChannel channel() {
return NettyChannelBuilder.forTarget("localhost")
.defaultLoadBalancingPolicy("round_robin")
.usePlaintext()
.build();
}
}
hope this helps.
Upvotes: 0
Reputation: 1187
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.
Upvotes: 4