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

前面已经分析了从配置层到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机制获取序列代器,对编码之后的数据进行序列代操作。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值