dubbo支持netty、mina、grizzly等网络框架,这也使得它的io抽象的非常复杂,什么exchange、transporter,让人一头雾水。从本质来看,也应该是抽象出dubbo自己的读、写、连接等IO事件,然后将各个网络框架的事件转发到dubbo的io事件上去。我们不去care这些抽象的细节,只关注dubbo是如何实现常见的几个网络io问题
连接管理
dubbo使用ConcurrentHashMap
来保存连接,key是ip:port, value是dubbo的抽象代表一个连接的channel。
ConcurrentHashMap
使用容器分离减少锁的粒度,通过cas空旋代替悲观锁,同时使用volatile做到读不加任何锁。连接创建的时候,put一个连接到map的性能应该是非常高
考虑服务端保存这些连接主要是想对这些连接做空闲连接管理。也即需要遍历下map中所有的连接,然后传给空闲连接task
public Collection<Channel> getChannels() {
Collection<Channel> chs = new HashSet<Channel>();
for (Channel channel : this.channels.values()) {
if (channel.isConnected()) {
chs.add(channel);
} else {
channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));
}
}
return chs;
}
上面的代码其实是对map做了一次copy,那这里直接使用CopyOnWriteArrayList
是不是会更好呢。因为rpc这种内部框架客户端连接跟连接数一般都是十分稳定的,并且一个服务器顶多也就几千的连接
空闲连接管理
dubbo服务端使用定时任务进行空闲连接管理,在服务器初始化的时候,会启动一个HashedWheelTimer
进行管理。dubbo没有使用netty的idle事件,主要原因可能是因为dubbo需要还支持mina、grizzly等这种网络框架,所以统一使用了定时任务来管理
心跳
心跳是客户端主动发起的,跟空闲连接管理一样,同样也是使用了HashedWheelTimer`的一个定时任务。
重连
通常rpc不需要断线重连,个人觉得原因是:
- 内部网络稳定,因网络问题断线概率低
- 可以在rpc调用的时候,再去获取一个连接,就算有断线重连这步还是无法避免
但是dubbo还是在客户端提供了断线重连的的定时任务ReconnectTimerTask
总结
简单总结下,dubbo为了兼容各个网络框架,使用各种定时任务进行连接管理。从常用io框架设计角度来看,把空闲、心跳、跟重连抽象成一个事件可能比定时任务更容易理解
网友评论