先来回忆一下, 一个rpc远程服务调用, 咱们一般是怎么写的:
以http协议为例, 我们会封装一个http协议的客户端, 服务端可能是直接用spring mvc编写的restful服务
然后客户端把服务的地址通过spring注入或者直接从配置文件里读取.
整个调用过程如下图
Dubbo作为远程服务调用框架, 为了能在各环节都能灵活扩展, 在不同协议上能将调用过程标准化,
需要考虑的问题可远没那么简单, 他将RPC过程进行了细化分解, 高度抽象
下面是我在学习了dubbo源码之后, 对几个核心概念的理解:
- 基本的, 定义客户端 服务端接口 Client, Server
- 为了兼容不同的协议, 对不同协议的网络传输调用, 抽象出Transporter接口, 绑定服务url与处理回调方法. 对上层屏蔽底层不同协议处理方法.
- 由于不同协议的交互数据, 行为不一致, 在传输层上, 又抽象了一层信息交互层(exchanger), 对上层提供了一致的消息传递处理方法, 一致的入参(Request), 回参(Response),消息回调方法(ExchangeHandler).
- 为了进一步简化远程服务调用, 屏蔽调用处理细节, 使能够像调用本地方法一样调用远程服务. 也出于服务注册发现管理的需要, 又抽象出了一层Protocol层, 官方称之为远程调用层, 将调用方法,调用参数,参数类型封装到Invocation, 将结果封装到Result对象中, 返回给上层调用.对Protocol的调用上层来说, protocol接口核心主要做了两件事:暴露服务以及获得远程服务的引用
- Invoker是Dubbo对方法的抽象, 在服务端, exporter通过protocol, 将invoker暴露出来.在客户端, 通过protocol获得一个远程服务引用, 封装为invoker给上层调用.
- 再往上, 在业务层的业务bean, 如果要像调用本地方法一样调用服务, 不可能直接调用invoker吧, 于是中间还有一层代理层, 代理层将invoker转换成实现了业务服务接口的代理实例.
- 为了能在服务调用过程中, 补充其他功能, 诸如记录日志, 权限控制,添加缓存,参数校验等, 使用过滤器模式对invoker进行处理返回.
下面是根据dubbo服务调用过程, 大致整理出来的一个比较粗略的调用关系图:
下面是Dubbo官方文档给出的说明, 将系统按功能分解为几个层次:
- config,配置层,对外配置接口,以ServiceConfig, ReferenceConfig为中心,可以直接new配置类,也可以通过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
配置, 注册中心, 路由,监控,序列化暂不关注.
来看看, 其他每一层大致的类结构设计思路:
服务代理层:
ServiceConfig, ReferenceConfig 等需要获取invoker实例, 或者需要将invoker转成对应的代理的时候, 都能看到ProxyFactory的影子.
如在ServiceConfig中, 通过代理获得invoker, 并暴露服务.
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
Exporter<?> exporter = protocol.export(invoker);
在ReferenceConfig中, 通过代理获得invoker对应的服务实例
private T createProxy(Map<String, String> map) {
……
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}
远程调用层(Protocol):
下面是Protocol层的几个核心接口, 可以看到Protocol接口主要负责将invoker包装为exporter对外发布,
同时也支持将远程服务url封装为invoker引用.
在protocol基类中, 会缓存暴露的及引用的invoker, 以便能统一对服务做管理, 比如destroy.
信息交换层(exchange):
下面是信息交换层的接口调用关系,
在服务端, 通过bind方法, 将一个服务url 绑定到一个消息处理方法.
在客户端, 通过connect方法, 同样把服务端返回的消息, 放到exchangeHandler中进行处理.
ExchangeHandler只有一个接口方法: reply, 从channel接收到request请求处理之后返回结果.
入参request是Request类型参数, 返回参数Object是Response类型, 在DecodeHandler中, 进行Request, Response类型的封装,解析.
public class HeaderExchanger implements Exchanger {
public static final String NAME = "header";
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
}
从DubboProtocol中ExchangeHandler可以看出, exchangeChannel可以得到invoker信息, 并从protocol中缓存的exporterMap里找到invoker引用.
网络传输层(transport):
Transporter有MinaTransporter, NettyTransporter等实现, 对不同协议的Server, Client做了封装:
public class MinaTransporter implements Transporter {
public static final String NAME = "mina";
public Server bind(URL url, ChannelHandler handler) throws RemotingException {
return new MinaServer(url, handler);
}
public Client connect(URL url, ChannelHandler handler) throws RemotingException {
return new MinaClient(url, handler);
}
}
以Netty为例, SimpleChannelHanlder是Netty自带的默认channel处理器, Dubbo设计了一个NettyHandler实现了dubbo的channelHandler与Netty channelHandler的对接.