A generic call (client generic call) refers to invoking the service when the caller does not have the service provider’s API (SDK) and can still obtain the call result. The caller can invoke the corresponding interface through a generic call by knowing the fully qualified class name and method name of the service interface.
Generic calls can initiate requests to all services through a universal GenericService interface. Typical use cases include:
Gateway Service: When building a gateway service, the service gateway acts as the caller for all RPC services without relying on the service provider’s API. Therefore, it requires support for generic calls.
Testing Platform: When creating a platform to test RPC calls, users can input group names, interfaces, method names, etc., to test the corresponding RPC services. Similar to the gateway, it should not depend on the service provider’s API, hence requiring support for generic calls.
Please refer to the complete source code of this example at dubbo-samples-generic-call.
The example includes the following Dubbo service definitions and implementations.
Service interface definition:
public interface HelloService {
String sayHello(String name);
CompletableFuture<String> sayHelloAsync(String name);
CompletableFuture<Person> sayHelloAsyncComplex(String name);
CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name);
}
Service implementation and publishing:
@DubboService
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "sayHello: " + name;
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
// ...
}
@Override
public CompletableFuture<Person> sayHelloAsyncComplex(String name) {
// ...
}
@Override
public CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name) {
// ...
}
}
For the above Dubbo service, we can initiate calls directly through the generic call API.
private GenericService genericService;
public static void main(String[] args) throws Exception {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
referenceConfig.setGeneric("true");
// do not wait for result, 'false' by default
referenceConfig.setAsync(true);
referenceConfig.setTimeout(7000);
genericService = referenceConfig.get();
}
public static void invokeSayHello() throws InterruptedException {
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
CountDownLatch latch = new CountDownLatch(1);
CompletableFuture<String> future = RpcContext.getContext().getCompletableFuture();
future.whenComplete((value, t) -> {
System.err.println("invokeSayHello(whenComplete): " + value);
latch.countDown();
});
System.err.println("invokeSayHello(return): " + result);
latch.await();
}
ReferenceConfig
, use setGeneric("true")
to enable generic calls.ReferenceConfig
, use referenceConfig.get()
to get the instance of the GenericService
class.$invoke
method to get the result.In Spring, there are various ways to expose services and discover services, such as XML and annotations. Here, XML is used as an example.
No changes are needed on the producer side.
The existing dubbo:reference
tag on the consumer side should have the generic=true
attribute added.
<dubbo:reference id="helloService" generic = "true" interface="org.apache.dubbo.samples.generic.call.api.HelloService"/>
Obtain the Bean container and retrieve the GenericService
instance via the Bean container.
Call the $invoke
method to get the result.
private static GenericService genericService;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/generic-impl-consumer.xml");
context.start();
// The name of the service corresponding bean is determined by the id of the xml tag.
genericService = context.getBean("helloService");
// Obtain the result.
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
}