dubbo原理
RPC原理
|
|
RPC框架的目标就是要2~8这些步骤都封装起来,这些细节对用户来说是透明的,不可见的。
netty通信原理
Netty是一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端。它极大地简化并简化了TCP和UDP套接字服务器等网络编程。
BIO:(Blocking IO)
NIO (Non-Blocking IO)
Selector 一般称 为选择器 ,也可以翻译为 多路复用器,
Connect(连接就绪)、Accept(接受就绪)、Read(读就绪)、Write(写就绪)
Netty基本原理:
dubbo原理
框架设计
图例说明
- 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
- 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
- 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
- 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。
各层说明
- config 配置层:对外配置接口,以
ServiceConfig
,ReferenceConfig
为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 - proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以
ServiceProxy
为中心,扩展接口为ProxyFactory
- registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为
RegistryFactory
,Registry
,RegistryService
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以
Invoker
为中心,扩展接口为Cluster
,Directory
,Router
,LoadBalance
- monitor 监控层:RPC 调用次数和调用时间监控,以
Statistics
为中心,扩展接口为MonitorFactory
,Monitor
,MonitorService
- protocol 远程调用层:封装 RPC 调用,以
Invocation
,Result
为中心,扩展接口为Protocol
,Invoker
,Exporter
- exchange 信息交换层:封装请求响应模式,同步转异步,以
Request
,Response
为中心,扩展接口为Exchanger
,ExchangeChannel
,ExchangeClient
,ExchangeServer
- transport 网络传输层:抽象 mina 和 netty 为统一接口,以
Message
为中心,扩展接口为Channel
,Transporter
,Client
,Server
,Codec
- serialize 数据序列化层:可复用的一些工具,扩展接口为
Serialization
,ObjectInput
,ObjectOutput
,ThreadPool
关系说明
- 在 RPC 中,Protocol 是核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。
- 图中的 Consumer 和 Provider 是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用 Client 和 Server 的原因是 Dubbo 在很多场景下都使用 Provider, Consumer, Registry, Monitor 划分逻辑拓普节点,保持统一概念。
- 而 Cluster 是外围概念,所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker,这样其它人只要关注 Protocol 层 Invoker 即可,加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响,因为只有一个提供者时,是不需要 Cluster 的。
- Proxy 层封装了所有接口的透明化代理,而在其它层都以 Invoker 为中心,只有到了暴露给用户使用时,才用 Proxy 将 Invoker 转成接口,或将接口实现转成 Invoker,也就是去掉 Proxy 层 RPC 是可以 Run 的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
- 而 Remoting 实现是 Dubbo 协议的实现,如果你选择 RMI 协议,整个 Remoting 都不会用上,Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层,Transport 层只负责单向消息传输,是对 Mina, Netty, Grizzly 的抽象,它也可以扩展 UDP 传输,而 Exchange 层是在传输层之上封装了 Request-Response 语义。
- Registry 和 Monitor 实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起。
启动解析、加载配置信息
xml配置走的是DubboNamespaceHandler进行初始化,而yml或properties走的是EnableDubbo接口进行初始化。接下来讲解xml配置的初始化方式。
DubboNamespaceHandler -> DubboBeanDefinitionParser
配置文件是如何被解析的?
配置文件是spring的一个配置文件,spring在启动的时候会去解析配置文件,有一个总接口org.springframework.beans.factory.xml.BeanDefinitionParser
从继承结构上可以看到org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser实现了这个接口。
org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#init定义了各个标签对应的结构
|
|
org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, java.lang.Class<?>, boolean)会解析各个标签属性到对应的xxxConfig
服务暴露
afterPropertiesSet
解析配置,ServiceBean解析
org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#init
|
|
ServiceBean
|
|
InitializingBean
在容器创建完ServiceBean对象后会调用afterPropertiesSet方法
ServiceBean继承自ServiceConfig,afterPropertiesSet方法主要作用是将之前解析的所有配置保存在ServiceBean中。
onApplicationEvent
ApplicationListener<ContextRefreshedEvent>
当ioc容器刷新完成也就是所有对象创建完成时会来回调实现这个接口的方法onApplicationEvent
这个方法里面主要是实现暴露服务逻辑
org.apache.dubbo.config.ServiceConfig#doExportUrls
|
|
org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
|
|
RegistryProtocol
org.apache.dubbo.registry.integration.RegistryProtocol#export
|
|
DubboProtocol
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#export
|
|
org.apache.dubbo.registry.support.ProviderConsumerRegTable
|
|
小结
DubboProtocol会在底层启用netty服务器监听20880端口
RegistryProtocol会保存注册信息到注册表中
服务引用
Service对应ServiceBean, Reference对应ReferenceBean
|
|
ReferenceBean是一个FactoryBean,获取UserService就会调用FactoryBean的getObject方法
org.apache.dubbo.config.spring.ReferenceBean#getObject ↓
org.apache.dubbo.config.ReferenceConfig#init
|
|
org.apache.dubbo.config.ReferenceConfig#createProxy
|
|
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#protocolBindingRefer
|
|
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#initClient
|
|
RegistryProtocol
org.apache.dubbo.registry.integration.RegistryProtocol#doRefer
|
|
服务调用
展开总设计图的红色调用链,如下:
-
将methods和args包装成RpcInvocation对象
-
FailoverClusterInvoker.invoke(invocation)
-
根据负载均衡选择一个invoker
-
DubboInvoker.invoke(invocation)
1 2 3 4 5 6 7 8
... AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv); CompletableFuture<Object> responseFuture = currentClient.request(inv, timeout); asyncRpcResult.subscribeTo(responseFuture); // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter FutureContext.getContext().setCompatibleFuture(responseFuture); return asyncRpcResult; ...
由于netty是基于异步事件驱动的,所以将结果存放在java8才有的CompletableFuture中
-
回到一开始的invoker,org.apache.dubbo.rpc.proxy.InvokerInvocationHandler#invoke
1 2 3 4 5 6
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); ... return invoker.invoke(new RpcInvocation(method, args)).recreate(); }
org.apache.dubbo.rpc.AsyncRpcResult#recreate
1 2 3 4 5 6 7 8 9 10
public Object recreate() throws Throwable { RpcInvocation rpcInvocation = (RpcInvocation) invocation; FutureAdapter future = new FutureAdapter(this); RpcContext.getContext().setFuture(future); if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) { return future; } return getAppResponse().recreate(); }
Result
1
public interface Result extends CompletionStage<Result>, Future<Result>, Serializable {
getAppResponse
1 2 3 4 5 6 7 8 9 10 11
public Result getAppResponse() { try { if (this.isDone()) { return this.get(); } } catch (Exception e) { // This should never happen; logger.error("Got exception when trying to fetch the underlying result from AsyncRpcResult.", e); } return new AppResponse(); }
可以看到最终还是调用了Future的get方法,将netty client的异步调用改成了客户端的同步阻塞式调用。
总结
各种Invoker层层包装,底层使用netty通信~