导读:javascript
近日,在Apache Dubbo开发者沙龙杭州站的活动中,阿里巴巴中间件技术专家曹胜利(展图)向开发者们分享了Dubbo2.7版本的规划。java
本文将为你探秘 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版本以前的异步方式async
在2.6.x及以前的版本提供了必定的异步编程能力,包括Consumer端异步调用、参数回调、事件通知等。但当前的异步方式存在如下问题:ide
Future获取方式不够直接;异步编程
Future接口没法实现自动回调,而自定义ResponseFuture虽支持回调但支持的异步场景有限,如不支持Future间的相互协调或组合等;微服务
不支持Provider端异步
以Consumer端异步使用方式为例:
一、定义一个普通的同步接口并声明支持异步调用
public interface FooService { String findFoo(String name);} <dubbo:reference id="fooService" interface="com.alibaba.foo.FooService"> <dubbo:method name="findFoo" async="true" /> </dubbo:reference>
二、经过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(); }});
从这个简单的示例咱们能够体会到一些使用中的不便之处:
» 2.7.0基于CompletableFuture的加强
了解Java中Future演进历史的同窗应该知道,Dubbo 2.6.x及以前版本中使用的Future是在Java 5中引入的,因此存在以上一些功能设计上的问题,而在Java 8中引入的CompletableFuture进一步丰富了Future接口,很好的解决了这些问题。
Dubbo在2.7.0版本已经升级了对Java 8的支持,同时基于CompletableFuture对当前的异步功能进行了加强。
一、支持直接定义返回CompletableFuture的服务接口。经过这种类型的接口,咱们能够更天然的实现Consumer、Provider端的异步编程。
public interface AsyncService { CompletableFuture<String> sayHello(String name);}
二、若是你不想将接口的返回值定义为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实现,能够自动帮咱们自动生成异步方法定义。
三、一样的,若是你的原始接口定义不是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 + ", response from provider."); }).start(); return null; }}
在方法体的开始RpcContext.startAsync()启动异步,并开启新线程异步的执行业务逻辑,在耗时操做完成后经过asyncContext.write将结果写回。
四、RpcContext直接返回CompletableFuture
CompletableFuture<String> f = RpcContext.getContext().getCompletableFuture();
以上全部的加强,是在兼容已有异步编程的基础上进行的,所以基于2.6.x版本编写的异步程序不用作任何改造便可顺利运行。
元数据的改造主要是从适配微服务注册中心、配置中心分离的模型、减轻注册中心压力、提升服务治理能力和效率的角度来执行的。目前版本的Dubbo在注册中心的URL有数十个key/value的键值对,包含了一个服务的全部元数据。在大规模实践的基础上,咱们逐渐发现这样组织的元数据存在一些问题:
致使存储压力骤增,变动事件的推送效率明显降低;同时给订阅方带来了额外的计算压力,尤为是大规模场景下的内存,增加显著。
负责初始配置的同步,同时负责存储各类运行期配置规则。这一方面加重了注册中心的压力,另外一方面配置规则的灵活性也受到了必定的限制,同时也没法利用一些更专业的微服务配置中心带来的强大功能。
methods, pid, owner看起来都是为服务查询服务而注册的属性,但当咱们实际开发或操做服务管控系统时,却发现这样简陋的信息是很难知足查询治理需求的。咱们更多的属性,须要更丰富的注册数据。以methods为例,虽然方法列表的内容已经很长了,但当咱们要在OPS开发服务测试/mock功能时,却发现须要的方法签名等数据仍是没法获取。
归纳以上问题,咱们将URL中的元数据划分了三个部分:
接口的完整定义:包含接口名,接口所含的方法,以及方法所含的出入参信息。对于服务测试和服务mock有很是重要的做用。
须要将参数从provider端传递给消费者端,让消费者端感知到的。如token,timeout等。
只有在provider端或者消费者端须要使用的,如executes, document等。
配置中心是dubbo.properties的动态版本,支持的粒度包括全局的、应用级别的和服务级别的等维度。经过上面的元数据改造,配置中心支持,再加上原有的注册中心,Dubbo体系里就会存在:
理想状况下,注册中心将只用于关键服务信息(核心链路)的同步,进一步减轻注册中心的存储压力,提升地址同步效率,同时缓解当前因为URL冗余在大规模推送时形成的Consumer端内存计算压力。
解决当前配置和地址信息耦合的问题,经过抽象动态配置层,让开发者能够对接微服务场景下更经常使用的、更专业的配置中心,如Nacos, Apollo, Consul, Etcd等;提供更灵活的、更丰富的配置规则,包括服务、应用不一样粒度的配置,更丰富的路由规则,集中式管理的动态参数规则等。
对于纯粹的服务查询相关的数据,包括Consumer的服务订阅数据,每每都是注册后不可变的而且不须要节点间的同步,如当前URL能够看到的methods、owner等key以及全部的Consumer端URL。
所以咱们在2.7.0中引入了存储模块,专门用来存放这部分数据,这部分将会和新版本的Dubbo-ops密切整合,做为丰富的服务查询、测试等功能的数据基础,所以这部分的数据将会获得进一步的丰富。整体来讲否开启此功能对用户将是可选的,而且实现上也将是可扩展的,如咱们计划支持Redis, Zookeeper等。
Dubbo 提供了具备必定扩展性的路由规则,其中具备表明性的是条件路由和脚本路由。2.6.x及如下版本存在的问题:
从问题出发咱们从新设计,将原来的路由配置从注册中心迁往配置中心。明确了配置和服务发现的边界。新增了RouterChain,用于重构路由规则逻辑,新增应用级别路由,Tag路由优化等。针对服务级别的路由,精确到单个服务,避免了没法明确路由规则的问题。
咱们简单归纳下各个类的协做关系。
Dubbo 将在近期正式发布2.7.0版本,恰值Dubbo宣布重启一周年。这一年,Dubbo 共发布了13个版本,社区共有24位PPMC/Committer,144位Contributor,在北京、上海、深圳、成都和杭州举办了5场开发者沙龙,但技术开源的道路并无止境,咱们欢迎更多的开发者们能够参与进来,并到Dubbo meetup来进行分享,一块儿建设Dubbo生态。
原文连接本文为云栖社区原创内容,未经容许不得转载。