同步调用 是指调用线程在服务提供方返回结果前需要被阻塞,异步调用则是消费端发起调用后会马上返回。 (然后服务方处理完成之后,通过回调的形式将结果告知给消费方。)
在dubbo 2.6.*版本中
Future.get()方法获取到结果
案例
服务提供者
public static void main(String[] args) throws IOException {
//创建ServiceConfig实例
ServiceConfig serviceConfig=new ServiceConfig();
//设置应用程序配置
serviceConfig.setApplication(new ApplicationConfig("first-dubbo-provider"));
//设置服务注册中心信息
RegistryConfig registryConfig=new RegistryConfig("multicast://224.5.6.7:1234");
serviceConfig.setRegistry(registryConfig);
//设置接口与实现类
serviceConfig.setInterface(GreetingService.class);
serviceConfig.setRef(new GreetingServiceImpl());
//设置服务分组与版本
serviceConfig.setVersion("1.0.0");
serviceConfig.setGroup("dubbo");
//导出服务
//启动了nettyServer监听请求,并将服务注册到服务注册中心
serviceConfig.export();
//挂起线程 避免服务停止
System.out.println("server is started");
System.in.read();
}
服务消费者
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建服务引用对象实例
ReferenceConfig referenceConfig=new ReferenceConfig();
//设置应用信息
referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));
//设置服务注册中心 zookeeper://127.0.0.1:2181 广播模式 multicast://224.5.6.7:1234
referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
//设置服务接口和超时时间
referenceConfig.setInterface(GreetingService.class);
referenceConfig.setTimeout(5000);
//设置服务分组与版本
referenceConfig.setVersion("1.0.0");
referenceConfig.setGroup("dubbo");
//设置为异步
referenceConfig.setAsync(true);
//引用服务
GreetingService greetingService=referenceConfig.get();
//设置隐式参数
RpcContext.getContext().setAttachment("company","alibaba");
//调用服务
System.out.println(greetingService.sayHello("world"));
//等待结果
Future future=RpcContext.getContext().getFuture();
System.out.println(future.get());
}
Future
这一步从上下文获取future对象 然后从里面获取真正的值。
缺点就是 future.get()是一个阻塞的方法,调用该方法 在结果返回前,当前线程将一直处于阻塞状态。
在future对象上设置回调方法,当服务端完成操作的时候,就自动调用回调方法 不阻塞当前线程。
服务消费者
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建服务引用对象实例
ReferenceConfig referenceConfig=new ReferenceConfig();
//设置应用信息
referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));
//设置服务注册中心 zookeeper://127.0.0.1:2181 广播模式 multicast://224.5.6.7:1234
referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
//设置服务接口和超时时间
referenceConfig.setInterface(GreetingService.class);
referenceConfig.setTimeout(5000);
//设置服务分组与版本
referenceConfig.setVersion("1.0.0");
referenceConfig.setGroup("dubbo");
//设置为异步
referenceConfig.setAsync(true);
//引用服务
GreetingService greetingService=referenceConfig.get();
//设置隐式参数
RpcContext.getContext().setAttachment("company","alibaba");
//调用服务
System.out.println(greetingService.sayHello("world"));
//等待结果
((FutureAdapter)RpcContext.getContext().getFuture()).getFuture().setCallback(new ResponseCallback() {
@Override
public void done(Object o) {
System.out.println(o);
}
@Override
public void caught(Throwable throwable) {
System.out.println("error:"+throwable);
}
});
}
((FutureAdapter)RpcContext.getContext().getFuture()).getFuture().
获取到的是一个ResponseFuture对象,里面可以设置回调方法。
当前服务端执行完毕的时候,会调用该方法,不会阻塞业务线程。
这是借助了netty的异步通信机制,netty底层的io线程会在收到响应后自动回调注册的回调函数,不需要业务线程的干预。
使用了java8提供的completableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建服务引用对象实例
ReferenceConfig referenceConfig=new ReferenceConfig();
//设置应用信息
referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));
//设置服务注册中心 zookeeper://127.0.0.1:2181 广播模式 multicast://224.5.6.7:1234
referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
//设置服务接口和超时时间
referenceConfig.setInterface(GreetingService.class);
referenceConfig.setTimeout(5000);
//设置服务分组与版本
referenceConfig.setVersion("1.0.0");
referenceConfig.setGroup("dubbo");
//设置为异步
referenceConfig.setAsync(true);
//引用服务
GreetingService greetingService=referenceConfig.get();
//设置隐式参数
RpcContext.getContext().setAttachment("company","alibaba");
//调用服务
System.out.println(greetingService.sayHello("world"));
//等待结果
CompletableFuture completableFuture=RpcContext.getContext().getCompletableFuture();
completableFuture.whenComplete((v,t)->{
System.out.println("回调结果"+v);
});
}
CompletableFuture
从RpcContext中获取CompletableFuture ,使用whenComplete实现回调
可以让服务提供者返回的就是completableFuture对象,该接口就是一个异步接口。
定义异步接口 返回一个CompletableFuture对象
public interface GrettingServiceAsync {
CompletableFuture sayHello(String name);
}
实现类
public class GrettingServiceAsyncImpl implements GrettingServiceAsync {
//创建业务线程池
private final ThreadPoolExecutor bizThreadpool=new ThreadPoolExecutor(8,16,1, TimeUnit.SECONDS,new SynchronousQueue<>(),new NamedThreadFactory("biz-thread-poool"),new ThreadPoolExecutor.CallerRunsPolicy());
@Override
public CompletableFuture sayHello(String name) {
//为supplyAsync提供自定义线程池bizThreadpool 避免使用jdk公用线程池ForkJoinPool.commonPool();
//使用CompleTableFuture.supplyAsync让服务处理异步化
//保存当前线程的上下文
RpcContext context=RpcContext.getContext();
return CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello"+name+context.getAttachment("company");
},bizThreadpool);
}
}
服务提供者这里 返回一个CompletableFuture对象 ,且定义了一个线程池,然后使用CompletableFuture.supplyAsync接口,将业务逻辑放进去,返回一个future对象。 这里将本来在dubbo线程中处理的业务转到自定义的线程池中处理,这样dubbo线程就会立刻返回。
然后内部的业务逻辑 需要10秒后才能处理完
return CompletableFuture.supplyAsync(()->{ try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
然后发布服务 跟之前的服务提供者暴露没有什么不同 只是换了一个接口
public static void main(String[] args) throws IOException {
//创建ServiceConfig实例
ServiceConfig serviceConfig=new ServiceConfig();
//设置应用程序配置
serviceConfig.setApplication(new ApplicationConfig("first-dubbo-provider"));
//设置服务注册中心信息
RegistryConfig registryConfig=new RegistryConfig("multicast://224.5.6.7:1234");
serviceConfig.setRegistry(registryConfig);
//设置接口与实现类
serviceConfig.setInterface(GrettingServiceAsync.class);
serviceConfig.setRef(new GrettingServiceAsyncImpl());
//设置服务分组与版本
serviceConfig.setVersion("1.0.0");
serviceConfig.setGroup("dubbo");
//导出服务
//启动了nettyServer监听请求,并将服务注册到服务注册中心
serviceConfig.export();
//挂起线程 避免服务停止
System.out.println("server is started");
System.in.read();
}
服务消费者
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建服务引用对象实例
ReferenceConfig referenceConfig=new ReferenceConfig();
//设置应用信息
referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));
//设置服务注册中心 zookeeper://127.0.0.1:2181 广播模式 multicast://224.5.6.7:1234
referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
//设置服务接口和超时时间
referenceConfig.setInterface(GrettingServiceAsync.class);
referenceConfig.setTimeout(15000);
//设置服务分组与版本
referenceConfig.setVersion("1.0.0");
referenceConfig.setGroup("dubbo");
//设置为异步
referenceConfig.setAsync(true);
//引用服务
GrettingServiceAsync greetingService=referenceConfig.get();
//设置隐式参数
RpcContext.getContext().setAttachment("company","alibaba");
//调用服务
CompletableFuture completableFuture=greetingService.sayHello("world");
//等待结果
completableFuture.whenComplete((v,t)->{
System.out.println("回调结果"+v);
});
Thread.currentThread().join();
System.out.println("消费者主线程执行完毕");
}
消费端就可以获取到该接口的completableFuture结果,而不再需要经过RpcContext来做转换。
也就是dubbo支持completableFuture返回值的远程方法了。
需要注意的是:
referenceConfig.setTimeout(15000);
消费端设置的超时时间是15秒,然后greetingService.sayHello内部的执行时间是10秒,所以可以在超时时间前返回,如果greetingService.sayHello内部的处理时间大于了超时时间,那么会立即返回null ,dubbo内部有一个检测线程会检测调用时间大于超时时间的调用接口。
还有一点需要注意的是,这里用了 Thread.currentThread().join(); 来保持main方法在执行完毕之后不退出,不然会因为还没到接口返回,main方法就已经结束掉了。
这里服务端也是异步执行
服务提供者
public class GrettingServiceRpcContextImpl implements GreetingService {
@Override
public String sayHello(String name) {
AsyncContext asyncContext= RpcContext.startAsync();
new Thread(()->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
asyncContext.write("hello "+ name);
}).start();
return null;
}
@Override
public Result testGeneric(Pojo pojo) {
return null;
}
}
消费端获取的时候还是使用
RpcContext.getContext().getFuture().get()
来获取返回值 最终得到的也是一个future
但是服务端AsyncContext asyncContext= RpcContext.startAsync();
开启服务异步执行,返回AsyncContext方法 不会占用dubbo线程池里面的线程。