grpc-ecosystem / grpc-spring

Spring Boot starter module for gRPC framework.
https://grpc-ecosystem.github.io/grpc-spring/
Apache License 2.0
3.48k stars 812 forks source link

@GrpcClient with specific address #937

Open anh-joe-nguyen opened 1 year ago

anh-joe-nguyen commented 1 year ago

My use case is I have a big task and want to break it into smaller task and spread them to all instances. I'm using zookeeper for service discovery and can get all service instance information. Is there anyway that I can pass my instance info to @GrpcClient and trigger rpc call?

ST-DDT commented 1 year ago

Use a spring DiscoveryClient implementation of zookeeper or implement this file with Zookeeper: DiscoveryClientNameResolver + it's factory.

Does that answer your question?

anh-joe-nguyen commented 1 year ago

@ST-DDT tks for reply, let me try

anh-joe-nguyen commented 1 year ago

@ST-DDT As I understand your suggestion is the way of configuration to choose target address when application start. In my case I want to do it in runtime. Something like:

List<ZookeeperServiceInstance> serviceInstances = discoveryClient.getInstances("myService");
for (ZookeeperServiceInstance instance : serviceInstances) {
    GrpcClient gprcClient = GrpcClientFactory.createFrom(instance);
    gprcClient.myFunction("custom-param-for-each-instance");
}
ST-DDT commented 1 year ago

That might be a way to do it, but IMO you should do the following:

 public NameResolver newNameResolver(final URI targetUri, final NameResolver.Args args) {
        if ("zookeeper".equals(targetUri.getScheme())) {
            final String serviceName = targetUri.getPath();
            if (serviceName == null || serviceName.length() <= 1 || !serviceName.startsWith("/")) {
                throw new IllegalArgumentException("Incorrectly formatted target uri; "
                        + "expected: '" + "zookeeper"+ ":[//]/<service-name>'; "
                        + "but was '" + targetUri.toString() + "'");
            }
            final ZookeeperClientNameResolver nameResolver = newNameResolver(serviceName.substring(1), args);
            this.discoveryClientNameResolvers.add(nameResolver);
            return nameResolver;
        }
        return null;
    }

https://github.com/yidongnan/grpc-spring-boot-starter/blob/bfe74b293f05707979581ab170ff8ea3e9582499/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/nameresolver/DiscoveryClientNameResolver.java#L303

(with zookeeper)

anh-joe-nguyen commented 1 year ago

@ST-DDT as I understand, your suggest is for discovering all instance using grpc.client.service.address no? but what I want to achieve is to select specify stub instance to trigger in runtime

below code is working fine for me

@GrpcClient("service")
private ApplicationServiceGrpc.ApplicationServiceBlockingStub stub;

public void test() {
    stub.doSomething(MyRequest.newBuilder().setParam("param-for-instance-1").build());
    stub.doSomething(MyRequest.newBuilder().setParam("param-for-instance-2").build());
}

with above approach, I cannot specify which instance will trigger doSomething() hence param-for-instance-1 is not guarantee to send to instance 1

now I'm trying

@Autowired
private DiscoveryClient discoveryClient;

public void test() {
    for (ServiceInstance instance : discoveryClient.getInstances("service")) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress(instance.getHost(), 9090).usePlaintext().build();
        ApplicationServiceGrpc.ApplicationServiceBlockingStub stub = ApplicationServiceGrpc.newBlockingStub(channel);
        stub.doSomething(MyRequest.newBuilder().setParam("param-for-instance-" + instance.getHost()).build());
    }
}

but server return error UNAVAILABLE: No servers found for localhost:9090 even though there is gRPC Server started, listening on address: *, port: 9090 log when start up