先看看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
前面已经分析了从配置层到protocol层的调用过程,这篇来分析protocol层到serialize层的dubbo通信的过程。
这里以默认的dubbo协议,netty通讯框架为例,讨论dubbo的通信过程。
提供者服务端的启动过程
1. (
protocol层) DubboProtocol中createServer方法创建返回,并传入ExchangeHandler回调reply方法,处理客户端的请求。 server = Exchangers.bind(url, requestHandler)
2. (
exchange层)
调用HeaderExchanger的bind方法,创建一个HeaderExchangeServer
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
3. 传入的ChannelHandler 通过包装的方法,形成一个调用链, DecodeHandler中是对数据的反序代操作,HeaderExchangeHandler中对dubbo协议的数据进行处理,并回调reply方法。
4. (
transport层)构造方法中,调用Transporters.bind(),通过spi查找,会调用NettyTransporter.bind()方法, 并直接实例代一个NettyServer。
public Server bind(URL url, ChannelHandler listener) throws RemotingException {
return new NettyServer(url, listener);
}
5. NettyServer调用父类AbstractServer的构造方法, 从url中拿的一系统参数,回调doOpen方法开启服务,NettyServer的doOpen方法则是调用netty的api,开启了netty的服务端。
public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
6. ChannelHandlers.wrapInternal对handler再一次进行了包装,MultiMessageHandler负责对多条消息的处理,HeartbeatHandler负责心跳包的处理,spi构造出AllChannelHandler,负责对事件进行派发。
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
7. 第2步中实例代HeaderExchangeServer对象,构造方法中会调用startHeatbeatTimer方法,启动一个定时器线程,定时发送心跳包。
消费者客户端的启动过程
1. (
protocol层)
DubboProtocol.refer()方法根据接口类型与提供者地址实例代DubboInvoker,
并调用initClient方法初始代客户端连接。
2. (
exchange层)
调用HeaderExchanger的connect方法,创建一个HeaderExchangeClient。
3. (同服务端一样)传入的ChannelHandler 通过包装的方法,形成一个调用链, DecodeHandler中是对数据的反序代操作,HeaderExchangeHandler中对dubbo协议的数据进行处理,并回调reply方法。
4. (
transport层)构造方法中,调用Transporters.connect(),通过spi查找,会调用NettyTransporter.connect()方法, 并直接实例代一个NettyClient。
5. NettyClient调用父类AbstractClient的构造方法, 从url中拿的一系统参数,回调doOpen方法开启服务,NettyClient的doOpen方法则是调用netty的api,开启了netty的客户端。接回调doConnect方法创建与客户端的连接。
6. (同服务端一样)ChannelHandlers.wrapInternal对handler再一次进行了包装,MultiMessageHandler负责对多条消息的处理,HeartbeatHandler负责心跳包的处理,spi构造出AllChannelHandler,负责对事件进行派发。
7. 第2步中实例代HeaderExchangeClient对象,构造方法中会调用startHeatbeatTimer方法,启动一个定时器线程,定时发送心跳包。
8. 第1步实例代的
DubboInvoker的doInvoke方法中客户端会请求服务端的, 实际调用HeaderExchangeClient.send()->NettyChannel.send()->org.jboss.netty.channel.Channel.write(),最终调用netty的api发送请求。
消息的派发
消息的派发由spi机制查找具体的
Dispatcher来实现。
ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)
如果事件处理的逻辑能迅速完成,并且不会发起新的IO请求,比如只是在内存中记个标识,则直接在IO线程上处理更快,因为减少了线程池调度。但如果事件处理逻辑较慢,或者需要发起新的IO请求,比如需要查询数据库,则必须派发到线程池,否则IO线程阻塞,将导致不能接收其它请求。
如果用IO线程处理事件,又在事件处理过程中发起新的IO请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。
- Dispatcher
- all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
- direct 所有消息都不派发到线程池,全部在IO线程上直接执行。
- message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在IO线程上执行。
- execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在IO线程上执行。
- connection 在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
- ThreadPool
- fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
- cached 缓存线程池,空闲一分钟自动删除,需要时重建。
- limited 可伸缩线程池,但池中的线程数只会增长不会收缩。(为避免收缩时突然来了大流量引起的性能问题)。
协议编码方式
dubbo的编解码由Codec接口来实现,,执行的入口在启动netty服务时设置的handler回调。
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
具体实现在InternalEncoder与InternalDecoder中,调用
Codec的encode与decode..
数据的序列代实现
序列代的执行在编解码之前执行,具体实现在ExchangeCodec的encodeRequest,encodeResponse等方法中。
public static Serialization getSerialization(URL url) {
return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
}
通过spi机制获取序列代器,对编码之后的数据进行序列代操作。