前面咱们搭建了基于 Spring 和Dubbo API 方式简单的简单调用。服务消费端引入了一个 SDK 二方包(api.jar),里面存放着服务提供端提供的全部接口类,之因此须要引入接口类是由于服务消费端通常是基于接口使用 JDK 代理实现远程调用的。api
泛化接口调用方式主要在服务消费端没有 API 接口类及模型类元(好比入参和出参的 POJO 类)的状况下使用。其参数及返回值中没有对应的 POJO 类,因此全部 POJO 均转换为 Map 表示。使用泛化调用时候服务消费模块再也不须要引入 SDK 二方包。多线程
下面基于 Dubbo API 实现异步调用,在 Consumer 模块里面 TestConsumerApiGeneric 是泛化调用的方式,代码以下:并发
上面代码中,因为 sayHello 的参数是 String,没有很好的体现参数转换为 Map,下面咱们具体来讲下 POJO 参数转换 Map 的含义。app
好比服务提供者提供的一个接口的 testPojo(Person person) 方法的参数为以下所示:框架
package com.test;public class PersonImpl implements Person {private String name;private String password;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
则 POJO 数据:dom
Person person = new PersonImpl();person.setName("lawt");person.setPassword("password");
正常状况下调用接口是使用:异步
servicePerson.testPojo(person);
泛化调用下须要首先转换 person 为 Map,以下表示:ide
Map<String, Object> map = new HashMap<String, Object>();// 注意:若是参数类型是接口,或者List等丢失泛型,可经过class属性指定类型。map.put("class", "com.test.PersonImpl");map.put("name", "lawt");map.put("password", "password");
而后使用下面方法进行泛化调用:测试
servicePerson.$invoke("testPojo", new String[]{"com.tian.dubbo.domain.Person"}, new Object[]{map});
泛化调用一般用于框架集成,好比:实现一个通用的服务测试框架,可经过 GenericService 调用全部服务实现,而不须要依赖服务实现方提供的接口类以及接口的入参和出参的 POJO 类。this
不管前面咱们讲解的正常调用仍是泛化调用,都是同步调用,也就是服务消费方发起一个远程调用后,调用线程要被阻塞挂起,直到服务提供方返回。
本节讲解下服务消费端异步调用,异步调用是指服务消费方发起一个远程调用后,不等服务提供方返回结果,调用方法就返回了,也就是当前线程不会被阻塞,这就容许调用方同时调用多个远程方法。
在 Consumer 模块里面 TestConsumerAsync 是泛化调用,代码以下:
public class TestConsumerAsync { public static void main(String[] args) throws InterruptedException, ExecutionException { // 当前应用配置 ApplicationConfig application = new ApplicationConfig(); application.setName("dubboConsumer"); // 链接注册中心配置 RegistryConfig registry = new RegistryConfig(); registry.setAddress("127.0.0.1:2181"); registry.setProtocol("zookeeper"); // 引用远程服务 ReferenceConfig<UserServiceBo> reference = new ReferenceConfig<UserServiceBo>(); reference.setApplication(application); reference.setRegistry(registry); reference.setInterface(UserServiceBo.class); reference.setVersion("1.0.0"); reference.setTimeout(3000); //(1)设置为异步调用 reference.setAsync(true); // 和本地bean同样使用xxxService UserServiceBo userService = reference.get(); long startTime = System.currentTimeMillis() / 1000; // (2)由于异步调用,此处返回null System.out.println(userService.sayHello("哈哈哈")); // 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future Future<String> userServiceFutureOne = RpcContext.getContext().getFuture(); // (3)由于异步调用,此处返回null System.out.println(userService.sayHello2("哈哈哈2")); // 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future Future<String> userServiceFutureTwo = RpcContext.getContext().getFuture(); // (4)阻塞到get方法,等待结果返回 System.out.println(userServiceFutureOne.get()); System.out.println(userServiceFutureTwo.get()); long endTime = System.currentTimeMillis() / 1000; System.out.println("costs:" + (endTime - startTime)); }}
运行上面代码,输出以下图所示:
其中代码(2)(3)处输出 null,说明开启异步调用后调用方直接返回 null。
输出 costs:2
说明异步调用生效了,由于 sayHello 和 sayHello2 方法内都 sleep 了2s,若是是顺序调用则会耗时至少4s,这里耗时2s说明两次调用是并发进行的。
异步调用是基于 NIO 的非阻塞实现并行调用,客户端不须要启动多线程便可完成并行调用多个远程服务,相对调用不一样的服务使用不一样线程来讲开销较小。