我准备战斗到最后,不是因为我勇敢,是我想见证一切。 ——双雪涛《猎人》
[TOC]
Thinking
- 一个技术,为什么要用它,解决了那些问题?
- 如果不用会怎么样,有没有其它的解决方法?
- 对比其它的解决方案,为什么最终选择了这种,都有何利弊?
- 你觉得项目中还有那些地方可以用到,如果用了会带来那些问题?
- 这些问题你又如何去解决的呢?
本文基于Dubbo 2.6.7-SNAPSHOT
本文主要基于《Dubbo 开发指南 —— 框架设计》
1、整体设计
图例说明:
- 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
- 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
- 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
- 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。
1.1 各层说明
- ========================== Business==============================
- Service业务层:业务代码的接口与实现。暴露在外实际的使用Dubbo层。
- ========================== RPC==============================
- config 配置层:对外配置接口,以
ServiceConfig
,ReferenceConfig
为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类- dubbo-config 模块实现
- proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以
ServiceProxy
为中心,扩展接口为ProxyFactory
- dubbo-rpc 模块实现
- registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为
RegistryFactory
,Registry
,RegistryService
- dubbo-registry 模块实现
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以
Invoker
为中心,扩展接口为Cluster
,Directory
,Router
,LoadBalance
- dubbo-cluster 模块实现
- monitor 监控层:RPC 调用次数和调用时间监控,以
Statistics
为中心,扩展接口为MonitorFactory
,Monitor
,MonitorService
- dubbo-monitor 模块实现
- protocol 远程调用层:封装 RPC 调用,以
Invocation
,Result
为中心,扩展接口为Protocol
,Invoker
,Exporter
- dubbo-rpc 模块实现
- ========================== Remoting============================
- exchange 信息交换层:封装请求响应模式,同步转异步,以
Request
,Response
为中心,扩展接口为Exchanger
,ExchangeChannel
,ExchangeClient
,ExchangeServer
- dubbo-remoting-api 模块定义接口
- transport 网络传输层:抽象 mina 和 netty 为统一接口,以
Message
为中心,扩展接口为Channel
,Transporter
,Client
,Server
,Codec
- dubbo-remoting-api 模块定义接口
- serialize 数据序列化层:可复用的一些工具,扩展接口为
Serialization
,ObjectInput
,ObjectOutput
,ThreadPool
- dubbo-common 模块实现。
1.2 关系说明
在 RPC 中,Protocol 是核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。
- dubbo-rpc 模块可以独立完成该功能
图中的 Consumer 和 Provider 是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用 Client 和 Server 的原因是 Dubbo 在很多场景下都使用 Provider, Consumer, Registry, Monitor 划分逻辑拓普节点,保持统一概念。
而 Cluster 是外围概念,所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker,这样其它人只要关注 Protocol 层 Invoker 即可,加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响,因为只有一个提供者时,是不需要 Cluster 的
- dubbo-cluster 模块提供的是非必需的功能,移除不会影响到其它模块。RPC模块也可以正常运行。
Proxy 层封装了所有接口的透明化代理,而在其它层都以 Invoker 为中心,只有到了暴露给用户使用时,才用 Proxy 将 Invoker 转成接口,或将接口实现转成 Invoker,也就是去掉 Proxy 层 RPC 是可以 Run 的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
- 简单粗暴的说,Proxy 会拦截
service.doSomething(args)
的调用,“转发”给该 Service 对应的 Invoker ,从而实现透明化的代理。
而 Remoting 实现是 Dubbo 协议的实现,如果你选择 RMI 协议,整个 Remoting 都不会用上,Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层,Transport 层只负责单向消息传输,是对 Mina, Netty, Grizzly 的抽象,它也可以扩展 UDP 传输,而 Exchange 层是在传输层之上封装了 Request-Response 语义。
Registry 和 Monitor 实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起
2、核心流程
2.1 调用链
- 垂直分层如下:
- 下方 淡蓝背景( Consumer ):服务消费方使用的接口
- 上方 淡绿色背景( Provider ):服务提供方使用的接口
- 中间 粉色背景( Remoting ):通信部分的接口
- 自 LoadBalance 向上,每一行分成了多个相同的 Interface ,指的是负载均衡后,向 Provider 发起调用。
- 左边 括号 部分,代表了垂直部分更细化的分层,依次是:Common、Remoting、RPC、Interface 。
- 右边 蓝色虚线( Init ) 为初始化过程,通过对应的组件进行初始化。例如,ProxyFactory 初始化出 Proxy 。
2.2 暴露服务
服务提供方暴露服务蓝色初始化链(Init)时序图:
2.3.4 步 为本文重点:getInvoker()
2.3 引用服务
消费方引用服务的蓝色初始化链
第5步:refer() 第12步 都为本文重点。
3、领域模型
本章分享的位于Dubbo-rpc-api
目录中,如下图红框部分:
在 Dubbo 的核心领域模型中:
- Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
- Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
- Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。
3.1 Invoker
com.alibaba.dubbo.rpc
Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它。
它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
public interface Invoker extends Node {
/**
* get service interface.
*
* #getInterface() 获取Service接口
*
* @return service interface.
*/
Class getInterface();
/**
* invoke.
* 调用方法
*
* @param invocation
* @return result
* @throws RpcException
*/
Result invoke(Invocation invocation) throws RpcException;
}
3.1.1 详解Invoker
在dubbo中,万物皆是Invoker,即便是Exporter也是由Invoker进化而成的
由于Invoker
在Dubbo领域模型中非常重要的一个概念,很多设计思路都是向它靠拢。
这一思想渗透在整个实现代码里。
下面简单的说明Invoker
的两种实现:服务提供的invoker和服务消费的invoker
结合Dubbo demo中的 消费和提供者代码来理解上图。
- 服务消费者代码:
public class DemoClientAction {
private DemoServer demoServer;
public void setDemoServer(DemoServer demoServer) {
this.demoServer = demoServer;
}
public void start() {
String hello = demoServer.sayHello("world");
}
}
- 上面代码中的 DemoService 就是上图中服务消费端的 Proxy,用户代码通过这个 Proxy 调用其对应的 Invoker,而该 Invoker 实现了真正的远程服务调用。
- 服务提供者代码:
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) throws RemoteException {
return "Hello " + name;
}
}
- 上面这个类会被封装成为一个 AbstractProxyInvoker 实例,并新生成一个 Exporter 实例。这样当网络通讯层收到一个请求后,会找到对应的 Exporter 实例,并调用它所对应的 AbstractProxyInvoker 实例,从而真正调用了服务提供者的代码。
3.1.2Invoker 类图
上文所提到的,在Dubbo中invoker
是一个非常重要的概念,既可以理解为万物皆为Invoker
。所以在Dubbo中的实现类非常多。
3.2 Invocation
com.alibaba.dubbo.rpc.Invocation
Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。
public interface Invocation {
/**
* get method name.
*
* 获取方法名
* @return method name.
* @serial
*/
String getMethodName();
/**
* get parameter types.
*
* 获取方法参数类型数组
* @return parameter types.
* @serial
*/
Class>[] getParameterTypes();
/**
* get arguments.
*
* 获取方法参数数组
* @return arguments.
* @serial
*/
Object[] getArguments();
/**
* get attachments.
*
* 获取隐式参数相关
* @return attachments.
* @serial
*/
Map getAttachments();
/**
* get attachment by key.
*
* @return attachment value.
* @serial
*/
String getAttachment(String key);
/**
* get attachment by key with default value.
*
* @return attachment value.
* @serial
*/
String getAttachment(String key, String defaultValue);
/**
* get the invoker in current context.
*
* 在当前上下文中获取调用者 invoker
*
* 获取对应的invoker对象
*
* @return invoker.
* @transient
*/
Invoker> getInvoker();
}
3.2.1 类图
- DecodeableRpcInvocation:是Dubbo协议独有的。
3.3 Result
com.alibaba.dubbo.rpc.Result
Result 是会话域,它持有调用过程中返回值,异常等。
RPC调用的结果集
public interface Result {
/**
* Get invoke result.
*
* 获取返回值
*
* @return result. if no result return null.
*/
Object getValue();
/**
* Get exception.
*
* 获取返回的异常
*
* @return exception. if no exception return null.
*/
Throwable getException();
/**
* Has exception.
*
* 判断是否存在异常
*
* @return has exception.
*/
boolean hasException();
/**
* Recreate.
*
*
* if (hasException()) {
* throw getException();
* } else {
* return getValue();
* }
*
*
* com.alibaba.dubbo.rpc.RpcResult
* RpcResult 中针对recreate() 的实现。
*
* @return result.
* @throws if has exception throw it.
* @see RpcResult // 具体实现
*/
Object recreate() throws Throwable;
/**
* @see com.alibaba.dubbo.rpc.Result#getValue()
* @deprecated Replace to getValue()
*/
@Deprecated
Object getResult();
/**
* 下面的getAttachments等方法 都是获取但会的隐式参数相关。
*/
/**
* get attachments.
*
* @return attachments.
*/
Map getAttachments();
/**
* get attachment by key.
*
* @return attachment value.
*/
String getAttachment(String key);
/**
* get attachment by key with default value.
*
* @return attachment value.
*/
String getAttachment(String key, String defaultValue);
}
#recreate()
的具体实现:
private Object result;
private Throwable exception;
@Override
public Object recreate() throws Throwable {
if (exception != null) {
throw exception;
}
return result;
}
3.3.1 类图
3.4 Filter
com.alibaba.dubbo.rpc.Filter
过滤器接口,和我们平时理解的 javax.servlet.Filter
基本一致。
/**
* Filter. (SPI, Singleton, ThreadSafe)
*/
@SPI
public interface Filter {
/**
* do invoke filter.
执行invoker的过滤逻辑。
*
*
* // before filter 自己实现
* Result result = invoker.invoke(invocation);
* // after filter 自己实现
* return result;
*
*
* @param invoker service
* @param invocation invocation.
* @return invoke result.
* @throws RpcException
* @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
*/
Result invoke(Invoker> invoker, Invocation invocation) throws RpcException;
}
3.4.1 类图
4.5 ProxyFactory
com.alibaba.dubbo.rpc.ProxyFactory代理工厂类
/**
* ProxyFactory. (API/SPI, Singleton, ThreadSafe)
*/
@SPI("javassist")
public interface ProxyFactory {
/**
* create proxy.
*
* 创建Proxy,在引用服务调用。
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
T getProxy(Invoker invoker) throws RpcException;
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
T getProxy(Invoker invoker, boolean generic) throws RpcException;
/**
* create invoker.
*
* 创建Invoker,在暴露服务时调用。
*
* @param
* @param proxy Service对象
* @param type Service接口类型
* @param url Service对应的Dubbo URL
* @return invoker
*/
@Adaptive({Constants.PROXY_KEY})
Invoker getInvoker(T proxy, Class type, URL url) throws RpcException;
}
服务消费者消费一个服务的详细过程
首先
ReferenceConfig
类的 init
方法调用 Protocol
的 refer
方法生成 Invoker
实例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker
转换为客户端需要的接口(如:HelloWorld)。
- 从图中我们可以看出,方法的
invoker
参数,通过 Protocol 将 Service接口 创建出 Invoker 。 - 通过创建 Service 的 Proxy ,实现我们在业务代理调用 Service 的方法时,透明的内部转换成调用 Invoker 的
#invoke(Invocation)
方法 - 服务提供者暴露服务的 主过程 如下图:
首先 ServiceConfig
类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory
类的 getInvoker
方法使用 ref 生成一个 AbstractProxyInvoker
实例,到这一步就完成具体服务到 Invoker
的转化。接下来就是 Invoker
转换到 Exporter
的过程。
从图中我们可以看出,该方法创建的 Invoker ,下一步会提交给 Protocol ,从 Invoker 转换到 Exporter 。
Dubbo 的实现
Dubbo 协议的 Invoker
转为 Exporter
发生在 DubboProtocol
类的 export
方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。
RMI 的实现
RMI 协议的 Invoker
转为 Exporter
发生在 RmiProtocol
类的 export
方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。
3.5.1 类图
从类图可以看出,Dubbo支持Javassist
和JDK Proxy
两种方式生成代理。
3.6 Protocol
com.alibaba.dubbo.rpc.Protocol Dubbo支持协议的顶层接口
Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口。
它负责 Invoker 的生命周期管理。
/**
* Protocol. (API/SPI, Singleton, ThreadSafe)
*
* Dubbo 支持RPC协议的 顶层接口
*/
@SPI("dubbo")
public interface Protocol {
/**
* Get default port when user doesn't config the port.
*
* 定义 默认端口
*
* @return default port
*/
int getDefaultPort();
/**
* Export service for remote invocation:
* 1. Protocol should record request source address after receive a request:
* RpcContext.getContext().setRemoteAddress();
* 2. export() must be idempotent, that is, there's no difference between invoking once and invoking twice when
* export the same URL
* 3. Invoker instance is passed in by the framework, protocol needs not to care
*
* 暴露远程调用服务:
* 1. 协议在接受请求时,应该记录请求的来源地址:RpcContext.getContext().setRemoteAddress();
* 2. export()必须是幂等的,也就是说,在暴露服务时,一次调用和两次调用时没有区别的,
* 3.传入的Invoker实例由框架实现并传入,协议无需关心。
*
* @param Service type 服务的类型
* @param invoker Service invoker 服务的执行体
* @return exporter reference for exported service, useful for unexport the service later
* @throws RpcException thrown when error occurs during export the service, for example: port is occupied
*/
@Adaptive
Exporter export(Invoker invoker) throws RpcException;
/**
* Refer a remote service:
引用远程服务
* 1. When user calls `invoke()` method of `Invoker` object which's returned from `refer()` call, the protocol
* needs to correspondingly execute `invoke()` method of `Invoker` object
* 2. It's protocol's responsibility to implement `Invoker` which's returned from `refer()`. Generally speaking,
* protocol sends remote request in the `Invoker` implementation.
* 3. When there's check=false set in URL, the implementation must not throw exception but try to recover when
* connection fails.
*
* 引用远程服务:
* 1. 当用户调用Refer()所返回的Invoker对象的invoke()方法时,协议需要相应地执行Invoker对象的Invoke()方法
* 2. 实现由`refer()`返回的`Invoker`是协议的责任。一般来说,协议在`Invoker`实现中发送远程请求。
* 3. 当url中设置了 check = false时,连接失败时不能抛出异常,且只能内部消化。
*
* @param Service type 服务的类型
* @param type Service class 服务的 class对象
* @param url URL address for the remote service 远程服务的url地址
* @return invoker service's local proxy 服务的本地代理
* @throws RpcException when there's any error while connecting to the service provider 当连接服务提供方失败时,抛出该异常。
*/
@Adaptive
Invoker refer(Class type, URL url) throws RpcException;
/**
* Destroy protocol:
* 1. Cancel all services this protocol exports and refers
* 2. Release all occupied resources, for example: connection, port, etc.
* 3. Protocol can continue to export and refer new service even after it's destroyed.
*
* 销毁/释放协议:
* 1. 取消该协议所有已经暴露和引用的服务。
* 2. 释放协议所占用的所有资源,比如:连接,端口等等。。
* 3. 协议即使销毁后也可以继续暴露并引用新服务。
*/
void destroy();
}
3.6.1 类图
从类图就可以很清楚的看出,Dubbo支持多协议。
3.7 Exporter
com.alibaba.dubbo.rpc.Exporter
Exporter,Invoker 暴露服务在Protocol上的对象。
/**
* Exporter. (API/SPI, Prototype, ThreadSafe)
*
* 暴露服务的 顶层接口
*
* @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
* @see com.alibaba.dubbo.rpc.ExporterListener
* @see com.alibaba.dubbo.rpc.protocol.AbstractExporter
*/
public interface Exporter {
/**
* get invoker.
*
* 获取对应的Invoker
*
* @return invoker
*/
Invoker getInvoker();
/**
* unexport.
*
* 取消 暴露
*
*
* getInvoker().destroy();
*
*/
void unexport();
}
3.7.1 类图
3.8 ExporterListener
com.alibaba.dubbo.rpc.ExporterListener
Exporter 监听器
/**
* ExporterListener. (SPI, Singleton, ThreadSafe)
*/
@SPI
public interface ExporterListener {
/**
* The exporter exported.
*
* 当服务暴露完成
*
* @param exporter
* @throws RpcException
* @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
*/
void exported(Exporter> exporter) throws RpcException;
/**
* The exporter unexported.
*
* 当服务取消暴露完成
*
* @param exporter
* @throws RpcException
* @see com.alibaba.dubbo.rpc.Exporter#unexport()
*/
void unexported(Exporter> exporter);
}
3.8.1 类图
3.9 InvokerListener
com.alibaba.dubbo.rpc.InvokerListener invoker监听器
/**
* InvokerListener. (SPI, Singleton, ThreadSafe)
* Invoker 监听器
*/
@SPI
public interface InvokerListener {
/**
* The invoker referred
*
* 当服务引用完成
*
* @param invoker
* @throws RpcException
* @see com.alibaba.dubbo.rpc.Protocol#refer(Class, com.alibaba.dubbo.common.URL)
*/
void referred(Invoker> invoker) throws RpcException;
/**
* The invoker destroyed.
*
* 当服务销毁引用完成
*
* @param invoker
* @see com.alibaba.dubbo.rpc.Invoker#destroy()
*/
void destroyed(Invoker> invoker);
}
3.9.1 类图
4、总结
本文重点概述了,Dubbo在服务的暴露与引用的大概流程。
本文仅供笔者本人学习,一起进步!
——努力努力再努力xLg
加油!