导读:
近日,在Apache Dubbo开发者沙龙杭州站的活动中,阿里巴巴中间件技术专家曹胜利(展图)向开发者们分享了Dubbo2.7版本的规划。
本文将为你探秘 Dubbo 2.7背后的思考和实现方式。
Dubbo 2.7 将围绕 异步支持优化、元数据改造,引入JDK8的特性、Netty4.0的特性以及MetricsAPI 5个方面提升服务调用和服务治理的效率,以及可扩展性,同时将修复社区提出的若干问题。
据悉,2.7.x会作为Dubbo在Apache社区的毕业版本,Dubbo将有机会成为继RocketMQ后,来自阿里巴巴的又一个Apache顶级项目(TLP)。
优化对异步的支持
基于Dubbo实现全异步编程,是在2.7.0版本中对现有异步方式增强后新引入的功能。之前的版本对异步支持用起来不是很友好,存在若干问题,2.7版本将基于JDK8 中的CompletableFuture做出一些针对性的增强,同时新增了@Dubboasync的注解,通过这个注解可以生成异步化相关的代码。

» 2.6.x版本之前的异步方式
在2.6.x及之前的版本提供了一定的异步编程能力,包括Consumer端异步调用、参数回调、事件通知等。但当前的异步方式存在以下问题:
Future获取方式不够直接;
Future接口无法实现自动回调,而自定义ResponseFuture虽支持回调但支持的异步场景有限,如不支持Future间的相互协调或组合等;
不支持Provider端异步
以Consumer端异步使用方式为例:
1、定义一个普通的同步接口并声明支持异步调用
public interface FooService { String findFoo(String name);} <dubbo:reference id="fooService" interface="com.alibaba.foo.FooService"> <dubbo:method name="findFoo" async="true" /> </dubbo:reference> 2、通过RpcContext获取Future
// 此调用会立即返回nullfooService.findFoo(fooId);// 拿到调用的Future引用,当结果返回后,会被通知和设置到此FutureFuture<Foo> fooFuture = RpcContext.getContext().getFuture();fooFuture.get(); 或
// 此调用会立即返回nullfooService.findFoo(fooId);// 拿到Dubbo内置的ResponseFuture并设置回调ResponseFuture future = ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture();future.setCallback(new ResponseCallback() { @Override public void done(Object response) { System.out.print(response); } @Override public void caught(Throwable exception) { exception.printStackTrace(); }}); 从这个简单的示例我们可以体会到一些使用中的不便之处:
- findFoo的同步接口,不能直接返回代表异步结果的Future,通过RpcContext进一步获取。
- Future只支持阻塞式的get()接口获取结果。
- 通过获取内置的ResponseFuture接口,可以设置回调。但获取ResponseFuture的API使用不便,且仅支持设置回调其他异步场景均不支持,如多个Future协同工作的场景等。
» 2.7.0基于CompletableFuture的增强
了解Java中Future演进历史的同学应该知道,Dubbo 2.6.x及之前版本中使用的Future是在Java 5中引入的,所以存在以上一些功能设计上的问题,而在Java 8中引入的CompletableFuture进一步丰富了Future接口,很好的解决了这些问题。
Dubbo在2.7.0版本已经升级了对Java 8的支持,同时基于CompletableFuture对当前的异步功能进行了增强。
1、支持直接定义返回CompletableFuture的服务接口。通过这种类型的接口,我们可以更自然的实现Consumer、Provider端的异步编程。
public interface AsyncService { CompletableFuture<String> sayHello(String name);} 2、如果你不想将接口的返回值定义为Future类型,或者存在定义好的同步类型接口,则可以额外定义一个异步接口并提供Future类型的方法。
public interface GreetingsService { String sayHi(String name);} @AsyncFor(GreetingsService.class)public interface GrettingServiceAsync extends GreetingsService { CompletableFuture<String> sayHiAsync(String name);} 这样,Provider可以只实现sayHi方法;而Consumer通过直接调用sayHiAsync可以拿到一个Future实例,Dubbo框架在Provider端会自动转换为对sayHi方法的调用。为每个同步方法提供一个异步方法定义会比较麻烦,更进一步的,利用Dubbo生态中的AnnotationProcessor实现,可以自动帮我们自动生成异步方法定义。
3、同样的,如果你的原始接口定义不是Future类型的返回值,Provider端异步也提供了类似Servlet3.0里的Async Servlet的编程接口: RpcContext.startAsync()。
public interface AsyncService { String sayHello(String name);} public class AsyncServiceImpl implements AsyncService { public String sayHello(String name) { final AsyncContext asyncContext = RpcContext.startAsync(); new Thread(() -> { asyncContext.write("Hello " + name
