什么是同步调用,也就是说调用线程在服务提供方结果返回前需要被阻塞,异步调用则是说消费者发起调用后会马上返回。本篇我们将介绍两种异步调用方式。
1. Dubbo 2.6.*版本提供的异步调用
首先我们看看第一种异步调用方式:首先写一个消费者基类
package cn.hackcloud.demo.dubbo;
import com.alibaba.fastjson.JSON;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
public class BasConsumer {
public static ReferenceConfig referenceConfig() {
//1.创建引用实例,并设置属性
ReferenceConfig reference = new ReferenceConfig<>();
reference.setApplication(new ApplicationConfig("consumer"));
reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
reference.setInterface("cn.hackcloud.demo.dubbo.rpc.IUserService");
reference.setGroup("hackcloud");
reference.setVersion("1.0");
return reference;
}
static void print(Object o) {
System.out.println(JSON.toJSONString(o));
}
}
/**
* 2.6版本异步调用
*
*@throwsExecutionException
*@throwsInterruptedException
*/public static void async() throws ExecutionException, InterruptedException {
ReferenceConfig reference = referenceConfig();
//2设置为异步
reference.setAsync(true);
//3.直接返回null
IUserService userService = reference.get();
System.out.println(userService.sync("1"));
//4.等待结果
Future future = RpcContext.getContext().getFuture();
print(future.get());
}
代码1创建引用实例,并且设置属性。代码2设置为异步调用。代码3进行服务引用并且调用sync方法,由于是异步调用,所以改方法马上返回null,代码4使用RpcContext.getContext().getFuture()对象,然后在需要获取真实响应的地方调用future.get()获取响应结果。
缺点:调用future.get()方法会使线程一直阻塞。
2. Dubbo 2.6.* 基于异步回调的方式调用
/**
* 2.6异步回调
*/public static void asyncCallBack() {
ReferenceConfig reference = referenceConfig();
//2设置为异步
reference.setAsync(true);
IUserService userService = reference.get();
//3.直接返回null
System.out.println(userService.sync("1"));
FutureAdapter adapter = (FutureAdapter) RpcContext.getContext().getFuture();
//4.回调函数
adapter.getFuture().setCallback(new ResponseCallback() {
@Override
public void done(Object response) {
print(response);
}
@Override
public void caught(Throwable exception) {
}
});
}
上面代码不同之处在于代码4,在FutureAdapter adapter =(FutureAdapter)RpcContext.getContext().getFuture().getFuture();获取的future对象上可以调用setCallBack()方法设置一个回调函数,该回调函数有2个方法,当远端正常返回响应结果后,会回调done()方法,其参数response就是响应结果值,当发起远程调用发生错误时会回调caught()方法以打印错误信息。
设置回调的这种方式不会阻塞业务线程,这是由于借助了Netty的异步通信机制,Netty底层的I/O线程会在接收到响应后自动回调注册的回调函数啊,不需要业务线程干预。
3. Dubbo 2.7.* 版本提供的异步调用
上面我们写了2个示例代码测试了Dubbo 2.7.0之前提供的异步调用方式,Future方式只支持阻塞式的get()接口获取结果,芮然通过获取内置的ResponseFuture接口可以设置回调,但获取ResponseFuture的API使用起来不方便,并且无法满足让多个Future协同工作的场景,功能比较单一。
基于Dubbo 2.7.0版本提供的CompletableFuture的异步调用代码如下:
/**
* 2.7异步回调
*/public static void asyncCallBackNew() {
ReferenceConfig reference = referenceConfig();
//设置为异步
reference.setAsync(true);
IUserService userService = reference.get();
userService.async("1");
//4.异步执行回调
CompletableFuture completableFuture = RpcContext.getContext().getCompletableFuture();
completableFuture.whenComplete((v, t) -> {
print(v);
});
}
上面代码4直接使用RpcContext.getContext().getCompletableFuture();
获取CompletableFuture类型的future,然后就可以基于CompletableFuture的能力做一些操作。这里调用whenComplete方法设置了回调函数,作用是当服务提供端产生响应结果后调用设置的回调函数,函数内判断如果异常t不为null,则打印异常信息,否则打印响应结果。