Dubbo高级特性概述
Dubbo解决了分布式场景RPC通信调用的问题,但是要满足各种业务场景还是不够的.场景:
- 支付业务需要自身迭代版本,比如1.0和2.0版本,在2.0版本做了大量性能改进,需要发布到性能测试环境与1.0版本做对比,这个时候需要框架提供服务隔离的能力.
- 客户端消费远程服务时不希望阻塞,这个时候业务方可以在线程池中发起RPC调用,但这样不够优雅,需要框架支持异步调用和回调
Dubbo提供了大量的高级特性.
服务分组和版本
Dubbo中提供的服务分组和版本是强隔离的,如果制定了服务分组和版本,则消费方调用也必须传递相同的分组名称和版本名称
参数回调
Dubbo支持异步参数回调,当消费方调用服务端方法时,允许服务端在某个时间点回调回客户端的方法.在服务端回调客户端时,服务端不会重新开启TCP连接,会复用已经建立的从客户端到服务端的TCP连接.下面是一个参数回调的例子:
异步回调服务端实现
1 | // 服务提供方暴露的接口 |
消费异步回调服务
1 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml"); |
隐式参数
Dubbo服务提供者或消费者启动时,配置元数据会生成URL,一般是不可变的.在很多场景中,在服务运行期需要动态改变属性值,在做动态路由和灰度发布场景中需要这个特性.Dubbo框架支持消费方在RpcContext#setAttachment方法中设置隐式参数,在服务端RpcContext#getAttachment方法中获取隐式传递
TODO:具体实现
异步调用
在客户端实现异步调用非常简单,在消费接口时配置异步标识,在调用时从上下文中获取Future对象,在期望结果返回时再调用阻塞方法Future.get()即可.
客户端异步调用:
1 | fooService.findFoo(fooId); |
Dubbo异步调用流程:
站在Dubbo客户端角度来说,直接发起RPC调用属于用户线程.用户线程①发起任意的远程方法调用,最终会通过I/O线程发送网络报文,在真实发送报文前会让用户线程中设置当前异步请求Future(③).因此在此用户线程发起下一个远程方法调用前,需要先保存异步Future对象(④).Dubbo框架会把异步请求放到DefaultFuture类中,当服务端响应或超时时,被挂起的用户线程将被唤醒(⑤).用户线程设置异步Future对象的逻辑在DubboInvoker#doInvoke方法中完成.(详细参阅DubboInvoker)
泛化调用
Dubbo繁华调用特性可以在不依赖服务接口API包的场景中发起远程调用.这种特性特别适合框架集成和网关类应用开发.Dubbo在客户端发起泛化调用并不要求服务端是泛化暴露.
泛化调用实例:
1 | ReferenceConfig<GenericService> ref = new ReferenceConfig<>(); |
泛化调用必需的参数主要包括:应用名称,注册中心,直接接口名称和泛化标识.在发起远程调用时,GenericService方法参数类型分别为真实方法名,真实方法参数类型签名和真实参数值.
这里需要注意的是,每次动态创建的GenericService实例比较重,需要建立TCP连接,处理注册中心订阅和服务列表等计算,因此需要缓存ReferenceConfig进行复用.
服务端在处理服务调用时,在GenericFilter拦截器中先把RpcInvocation中传递过来的参数类型和参数值提取出来,然后根据传递过来的接口名,方法名和参数类型查找服务端被调用的方法,获取真实方法后,主要提取真实方法参数类型(可能包含泛化类型),然后将参数值做Java类型转换.最后用解析后的参数值构造新的RpcInvocation对象发起调用
上下文信息
Dubbo上下文的获取和存储同样是基于JDKThreadLocal实现的.上下文中存放的是当前调用过程中所需的环境信息.RpcContext是一个ThreadLocal的临时状态记录器,当收到或发送RPC时,当前线程关联的RpcContext状态都会变化.比如:A调用B,B再调用C,则在B机器上,在B调用C之前,RpcContext记录的是A调用B的信息,在B调用C之后,RpcContext记录的是B调用C的信息.
服务端上下文的获取和使用:
1 | public class DemoServiceImpl implements DemoService { |
在客户端和服务端分别有一个拦截设置当前上下文信息,分别对应ConsumerContextFilter和ContextFilter.在客户端拦截器实现中,因为Invoker包含远程服务信息,因此直接设置远程IP等信息.在服务端拦截器中主要设置本地地址,这个时候无法获取远程调用地址.设置远程地址主要在DubboProtocol#ExchangeHandlerAdapter.reply方法中完成,可以直接通过channel.getRemoteAddress方法获取
Telnet操作
TODO
Mock调用
Dubbo提供服务容错的能力,通常用于服务降级,比如验权服务,当服务提供方"挂掉"后,客户端不抛出异常,而是通过Mock数据返回授权失败.
TODO
结果缓存
Dubbo框架提供了对服务调用结果尽心缓存的特性,用于加速热门数据的访问速度,Dubbo提供声明式缓存,以减少用户加载缓存的工作量.因为每次调用都会使用JSON.toString方法将请求参数转换成字符串,然后拼装唯一的key,用于缓存唯一键.
lru缓存策略是框架默认使用的.