泛化调用

泛化调用,用于在调用方没有服务方提供的 API(SDK)的情况下,对服务方进行调用

注意

泛化调用适用于老版本 dubbo 通信协议,如果您使用的是 3.3 及之后版本的 triple 协议,请直接使用 triple 自带的 http application/json 能力直接发起服务调用,相关示例可参考 网关接入说明

泛化调用(客户端泛化调用)是指在调用方没有服务提供方 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。调用方没有接口及模型类元,知道服务的接口的全限定类名和方法名的情况下,可以通过泛化调用调用对应接口。

使用场景

泛化调用可通过一个通用的 GenericService 接口对所有服务发起请求。典型使用场景如下:

  1. 网关服务:如果要搭建一个网关服务,那么服务网关要作为所有 RPC 服务的调用端。但是网关本身不应该依赖于服务提供方的接口 API(这样会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以需要泛化调用的支持。

  2. 测试平台:如果要搭建一个可以测试 RPC 调用的平台,用户输入分组名、接口、方法名等信息,就可以测试对应的 RPC 服务。那么由于同样的原因(即会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以平台本身不应该依赖于服务提供方的接口 API。所以需要泛化调用的支持。

使用方式

本示例的完整源码请参考 dubbo-samples-generic-call

示例中有以下 Dubbo 服务定义和实现

服务接口定义:

  1. public interface HelloService {
  2. String sayHello(String name);
  3. CompletableFuture<String> sayHelloAsync(String name);
  4. CompletableFuture<Person> sayHelloAsyncComplex(String name);
  5. CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name);
  6. }

服务具体实现并发布:

  1. @DubboService
  2. public class HelloServiceImpl implements HelloService {
  3. @Override
  4. public String sayHello(String name) {
  5. return "sayHello: " + name;
  6. }
  7. @Override
  8. public CompletableFuture<String> sayHelloAsync(String name) {
  9. // ...
  10. }
  11. @Override
  12. public CompletableFuture<Person> sayHelloAsyncComplex(String name) {
  13. // ...
  14. }
  15. @Override
  16. public CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name) {
  17. // ...
  18. }
  19. }

API 调用方式

针对以上 Dubbo 服务,我们可以通过泛化调用 API 直接发起调用。

  1. private GenericService genericService;
  2. public static void main(String[] args) throws Exception {
  3. ApplicationConfig applicationConfig = new ApplicationConfig();
  4. applicationConfig.setName("generic-call-consumer");
  5. RegistryConfig registryConfig = new RegistryConfig();
  6. registryConfig.setAddress("zookeeper://127.0.0.1:2181");
  7. ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
  8. referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
  9. applicationConfig.setRegistry(registryConfig);
  10. referenceConfig.setApplication(applicationConfig);
  11. referenceConfig.setGeneric("true");
  12. // do not wait for result, 'false' by default
  13. referenceConfig.setAsync(true);
  14. referenceConfig.setTimeout(7000);
  15. genericService = referenceConfig.get();
  16. }
  17. public static void invokeSayHello() throws InterruptedException {
  18. Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
  19. CountDownLatch latch = new CountDownLatch(1);
  20. CompletableFuture<String> future = RpcContext.getContext().getCompletableFuture();
  21. future.whenComplete((value, t) -> {
  22. System.err.println("invokeSayHello(whenComplete): " + value);
  23. latch.countDown();
  24. });
  25. System.err.println("invokeSayHello(return): " + result);
  26. latch.await();
  27. }
  1. 在设置 ReferenceConfig 时,使用 setGeneric("true") 来开启泛化调用
  2. 配置完 ReferenceConfig 后,使用 referenceConfig.get() 获取到 GenericService 类的实例
  3. 使用其 $invoke 方法获取结果
  4. 其他设置与正常服务调用配置一致即可

Spring 调用方式

Spring 中服务暴露与服务发现有多种使用方式,如 xml,注解。这里以 xml 为例。

  1. 生产者端无需改动

  2. 消费者端原有的 dubbo:reference 标签加上 generic=true 的属性。

  1. <dubbo:reference id="helloService" generic = "true" interface="org.apache.dubbo.samples.generic.call.api.HelloService"/>
  1. 获取到 Bean 容器,通过 Bean 容器拿到 GenericService 实例。

  2. 调用 $invoke 方法获取结果

  1. private static GenericService genericService;
  2. public static void main(String[] args) throws Exception {
  3. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/generic-impl-consumer.xml");
  4. context.start();
  5. //服务对应bean的名字由xml标签的id决定
  6. genericService = context.getBean("helloService");
  7. //获得结果
  8. Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
  9. }

最后修改 September 13, 2024: Refactor website structure (#2860) (1a4b998f54b)