• dubbo泛化调用之消费者传递JavaBean


    一、泛化调用概念

    泛化调用是指在调用方没有服务方提供的 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。

    二、使用场景

    泛化调用主要用于实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。比如如下场景:

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

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

    三、使用方式

    demo 可见 dubbo 项目中的示例代码

    API 部分以此 demo 为例讲解使用方式。

    1. 服务接口
    1. public interface HelloService {
    2. String sayHello(String name);
    3. CompletableFuture sayHelloAsync(String name);
    4. CompletableFuture sayHelloAsyncComplex(String name);
    5. CompletableFuture> sayHelloAsyncGenericComplex(String name);
    6. }
    2. 服务实现类
    1. public class HelloServiceImpl implements HelloService {
    2. @Override
    3. public String sayHello(String name) {
    4. return "sayHello: " + name;
    5. }
    6. @Override
    7. public CompletableFuture sayHelloAsync(String name) {
    8. CompletableFuture future = new CompletableFuture<>();
    9. new Thread(() -> {
    10. try {
    11. Thread.sleep(5000);
    12. } catch (InterruptedException e) {
    13. e.printStackTrace();
    14. }
    15. future.complete("sayHelloAsync: " + name);
    16. }).start();
    17. return future;
    18. }
    19. @Override
    20. public CompletableFuture sayHelloAsyncComplex(String name) {
    21. Person person = new Person(1, "sayHelloAsyncComplex: " + name);
    22. CompletableFuture future = new CompletableFuture<>();
    23. new Thread(() -> {
    24. try {
    25. Thread.sleep(5000);
    26. } catch (InterruptedException e) {
    27. e.printStackTrace();
    28. }
    29. future.complete(person);
    30. }).start();
    31. return future;
    32. }
    33. @Override
    34. public CompletableFuture> sayHelloAsyncGenericComplex(String name) {
    35. Person person = new Person(1, "sayHelloAsyncGenericComplex: " + name);
    36. GenericType genericType = new GenericType<>(person);
    37. CompletableFuture> future = new CompletableFuture<>();
    38. new Thread(() -> {
    39. try {
    40. Thread.sleep(5000);
    41. } catch (InterruptedException e) {
    42. e.printStackTrace();
    43. }
    44. future.complete(genericType);
    45. }).start();
    46. return future;
    47. }
    48. }
    3. 通过API使用泛化调用
    服务启动方
    1. 在设置 ServiceConfig 时,使用setGeneric("true")来开启泛化调用

    2. 在设置 ServiceConfig 时,使用 setRef 指定实现类时,要设置一个 GenericService 的对象。而不是真正的服务实现类对象

    3. 其他设置与正常 Api 服务启动一致即可

    1. private static String zookeeperAddress = "zookeeper://" + System.getProperty("zookeeper.address", "127.0.0.1") + ":2181";
    2. public static void main(String[] args) throws Exception {
    3. new EmbeddedZooKeeper(2181, false).start();
    4. //创建ApplicationConfig
    5. ApplicationConfig applicationConfig = new ApplicationConfig();
    6. applicationConfig.setName("generic-impl-provider");
    7. //创建注册中心配置
    8. RegistryConfig registryConfig = new RegistryConfig();
    9. registryConfig.setAddress(zookeeperAddress);
    10. //新建服务实现类,注意要使用GenericService接收
    11. GenericService helloService = new GenericImplOfHelloService();
    12. //创建服务相关配置
    13. ServiceConfig service = new ServiceConfig<>();
    14. service.setApplication(applicationConfig);
    15. service.setRegistry(registryConfig);
    16. service.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
    17. service.setRef(helloService);
    18. //重点:设置为泛化调用
    19. //注:不再推荐使用参数为布尔值的setGeneric函数
    20. //应该使用referenceConfig.setGeneric("true")代替
    21. service.setGeneric("true");
    22. service.export();
    23. System.out.println("dubbo service started");
    24. new CountDownLatch(1).await();
    25. }
    26. }
    泛化调用方

    步骤:

    1. 在设置 ReferenceConfig 时,使用 setGeneric("true") 来开启泛化调用

    2. 配置完 ReferenceConfig 后,使用 referenceConfig.get() 获取到 GenericService 类的实例

    3. 使用其 $invoke 方法获取结果

    4. 其他设置与正常 Api 服务启动一致即可

    1. private static GenericService genericService;
    2. public static void main(String[] args) throws Exception {
    3. //创建ApplicationConfig
    4. ApplicationConfig applicationConfig = new ApplicationConfig();
    5. applicationConfig.setName("generic-call-consumer");
    6. //创建注册中心配置
    7. RegistryConfig registryConfig = new RegistryConfig();
    8. registryConfig.setAddress("zookeeper://127.0.0.1:2181");
    9. //创建服务引用配置
    10. ReferenceConfig referenceConfig = new ReferenceConfig<>();
    11. //设置接口
    12. referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
    13. applicationConfig.setRegistry(registryConfig);
    14. referenceConfig.setApplication(applicationConfig);
    15. //重点:设置为泛化调用
    16. //注:不再推荐使用参数为布尔值的setGeneric函数
    17. //应该使用referenceConfig.setGeneric("true")代替
    18. referenceConfig.setGeneric(true);
    19. //设置异步,不必须,根据业务而定。
    20. referenceConfig.setAsync(true);
    21. //设置超时时间
    22. referenceConfig.setTimeout(7000);
    23. //获取服务,由于是泛化调用,所以获取的一定是GenericService类型
    24. genericService = referenceConfig.get();
    25. //使用GenericService类对象的$invoke方法可以代替原方法使用
    26. //第一个参数是需要调用的方法名
    27. //第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
    28. //第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
    29. Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
    30. //使用CountDownLatch,如果使用同步调用则不需要这么做。
    31. CountDownLatch latch = new CountDownLatch(1);
    32. //获取结果
    33. CompletableFuture future = RpcContext.getContext().getCompletableFuture();
    34. future.whenComplete((value, t) -> {
    35. System.err.println("invokeSayHello(whenComplete): " + value);
    36. latch.countDown();
    37. });
    38. //打印结果
    39. System.err.println("invokeSayHello(return): " + result);
    40. latch.await();
    41. }

    四、消费者如何传递JavaBean

    在Dubbo中,泛化调用可以实现对任何类型的服务方法的调用,包括复杂的参数类型。具体实现方式如下:

    1. 首先需要使用泛化调用的客户端对象(GenericService)来调用服务方法,并传入方法名和参数列表。参数列表可以使用Object数组来表示,如下所示

    1. GenericService genericService = referenceConfig.get();
    2. Object[] params = new Object[]{arg1, arg2, ...};
    3. Object result = genericService.$invoke(methodName, parameterTypes, params);

    2. 对于复杂参数类型,需要先生成对应的JavaBean类,并将参数封装到该类中。然后将该JavaBean对象作为参数列表中的一个元素传入泛化调用方法中,如下所示:

    1. @Data
    2. public class ComplexRequest implements Serializable {
    3. private Map content;
    4. private String key;
    5. private String value;
    6. private List users;
    7. }
    8. @Data
    9. public class User implements Serializable {
    10. private String name;
    11. private String age;
    12. }

     3. 消费者调用代码完整如下

    1. @Test
    2. public void testDubbo() {
    3. ApplicationConfig applicationConfig = new ApplicationConfig();
    4. applicationConfig.setName("generic-call-consumer");
    5. //创建注册中心配置
    6. RegistryConfig registryConfig = new RegistryConfig();
    7. registryConfig.setAddress("zookeeper://127.0.0.1:2181");
    8. //创建服务引用配置
    9. ReferenceConfig referenceConfig = new ReferenceConfig<>();
    10. //设置接口, 提供者的接口名
    11. referenceConfig.setInterface("com.test.api.provideFacade");
    12. applicationConfig.setRegistry(registryConfig);
    13. referenceConfig.setApplication(applicationConfig);
    14. //重点:设置为泛化调用
    15. //注:不再推荐使用参数为布尔值的setGeneric函数
    16. //应该使用referenceConfig.setGeneric("true")代替
    17. referenceConfig.setGeneric(true);
    18. //设置异步,不必须,根据业务而定。
    19. referenceConfig.setAsync(false);
    20. //设置超时时间
    21. referenceConfig.setTimeout(7000);
    22. //获取服务,由于是泛化调用,所以获取的一定是GenericService类型
    23. GenericService genericService = referenceConfig.get();
    24. //使用GenericService类对象的$invoke方法可以代替原方法使用
    25. //第一个参数是需要调用的方法名
    26. //第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
    27. //第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
    28. ComplexRequest entity = new ComplexRequest();
    29. Map map = new HashMap<>();
    30. map.put("key", "key");
    31. map.put("value", "value");
    32. entity.setContent(map);
    33. entity.setKey("key");
    34. entity.setValue("value");
    35. List users = new ArrayList<>();
    36. User user = new User();
    37. user.setName("name");
    38. user.setAge("18");
    39. users.add(user);
    40. entity.setUsers(user);
    41. // 构造泛化调用参数
    42. //消费者调用方的实体类
    43. String[] paramTypes = new String[]{"com.test.api.request.ComplexRequest"};
    44. Object[] params = new Object[]{entity};
    45. Object result = genericService.$invoke("method", paramTypes, params);
    46. System.out.println(result);
    47. }

    4. 提供者代码如下

    1. public interface ProvideFacade {
    2. PlainResult method(ComplexRequest req);
    3. }
    4. public class testFacadeImpl implements testFacade {
    5. @Override
    6. public PlainResult method(ComplexRequest req) {}
    7. }

    5. 执行单元测试

    运行消费者单元测试代码,请求提供者的dubbo泛化接口,进入到提供者机器,发现请求体已经传递过来,当提供者有多台机器时,请求会负载均衡,轮询到不同的机器上。

  • 相关阅读:
    电脑重装系统后鼠标动不了该怎么解决
    调试代码0
    【连网】Win10总是自动断网,检测默认网关不可用
    vue3 回顾
    【系统架构设计】架构核心知识: 1 系统工程与信息系统基础
    SpringMVC学习---第二课
    Visual Studio 2019部署桌面exe(笔记)
    Spring cloud properties与yml配置说明
    【以图会意】操作系统的加载流程
    【Android Camera开发】Android Automotive介绍
  • 原文地址:https://blog.csdn.net/xrq1995/article/details/132673612