凡是带有“池”的,比如数据库连接池、对象池、缓冲区池(后面可以看到
IBuffPool
)等等,都是为了避免资源的反复创建
/
销毁所带来的开销。需要为哪些资源对象建立“池”了?这些资源对象通常符合下面几个特性:
(1)
在应用中需要反复的被创建
/
销毁。
(2)
创建
/
销毁的开销比较大
(3)
应用中给定时刻,对该资源对象的数量要求比较大
(4)
资源对象最好是无状态的(
Stateless
),这样方便直接复用
AS(
回顾
)
将所有的功能服务请求转发给为该
AS
提供服务的
FS
群中的一个(参见。。。),然后
FS
将请求交给对应的功能插件处理。那么
AS
和
FS
之间的通信通过什么方式进行了?可选的方式有
.netRemoting
、
WebService
、
Tcp/Udp
等。基于效率和准确性的考虑,
WebService
和
Udp
是不大合适的。那么
TCP
和
.netRemoting
,到底选用哪个?我们知道
.netRemoting
底层也是基于
Tcp
或
Http
协议的,为了做到模拟本地方法调用的方式,
.NETRemoting
也做了很多转换的操作(堆栈帧《=》消息),导致了一些开销,而直接使用
Tcp
则可以避免,而且
AS
和
FS
之间的消息的格式是兼容的(主要是使用了完全相同的消息头,这就够了),也就是说一条消息从客户端发出,可以不需做任何转换就直接被
FS
的功能插件处理(加密、压缩不计在内)。
如你所想,
ESFramework
推荐的方式是
AS
和
FS
直接通过低层的
Tcp
进行通信。为了避免
Tcp
连接不断建立、销毁所带来的开销,
AS
和
FS
通信前,可以建立
Tcp
连接池。本文就关注
Tcp
连接池的原理和实现。
Tcp
连接池中存放的是
Tcp
连接――即
NetworkStream
对象,当应用需要使用时,就从
Tcp
连接池中租借“
Rent
”一条连接,用完后再归还“
GiveBack
”给连接池。
从上面的定义 可以看到 TcpStreamPool 从两个接口继承: ITcpPool 和 ITcpStreamPool 。先看看 ITcpStreamPool 的定义:
2 /// ITCPStreamPooltcp连接池用于管理大量的TCP连接
3 /// 作者:朱伟sky.zhuwei@163.com
4 /// sky2005.02.24
5 /// </summary>
6 public interface ITcpStreamPool
7 {
8 int ServerID{ get ; set ;}
9 int StreamCount{ get ; set ;} // 期望连接总数
10 int ActiveConnectionCount{ get ;} // 实际可用的连接数
11 IPEndPointFsIpe{ get ; set ;} // 功能服务器的IPE
12 int ReconnectSpan{ get ; set ;} // 分钟
13 bool IsActive{ get ;}
14
15 void ReConnect(); // 手动重连
16 void Initialize();
17 void DisposeConnections(); // 释放池中所有连接,可以通过ReConnect来重新建立连接
18 void SetStreamDamaged( int streamHashCode);
19
20 NetworkStreamRentTcpStream();
21 void GiveBackTcpStream( int streamHashCode); // 将tcp连接规还给连接池
22 }
AS 和每个 FS 之间都有一个连接池,每个功能服务器的区分是通过 ServerID 来的,所以连接池也有一个 ServerID 属性标志了本连接池是与哪个 FS 相连的。 ReconnectSpan 属性表明连接池要支持重连机制,即当连接池中的所有连接都断开后(可能是 FS 掉线引起的),连接池应能定时重连 FS ,直至该池中的所有连接重新建立。
如果应用从连接池 Rent 了一条连接,然后在使用的过程中该连接断开了,则应用应该调用连接池的 SetStreamDamaged 方法通知连接池该连接已不可用。 RentTcpStream 方法和 GiveBackTcpStream 方法是我们最常用的租借 / 归还连接的方法了。
注意,很多方法的参数中有 streamHashCode 参数,它是 NetworkStream 对象的 Hashcode ,系统中的每个 NetworkStream 对象的 HashCode 是不同的,并且,它的 HashCode 在 NetworkStream 对象的整个生命期间不变,所以可以使用 HashCode 唯一标志每个连接。
似乎,
ITcpStreamPool
接口已经反映了一个连接池的所有东西,是的。那么
ITcpPool
接口又起什么作用了?现看看
ITcpPool
的样子:
2 /// ITcpPool用于将一个TCP连接池和一组TCP连接池统一起来。这样消息分派器只需使用ITcpPool接口即可。
3 /// zhuweisky
4 /// </summary>
5 public interface ITcpPool
6 {
7 RentStreamResultRentTcpStream( int poolTypeKey, int serviceKey, out NetworkStreamstream, out int serverID); // poolTypeKey表示某个城市,serviceKey表示某项服务
8 void GiveBackTcpStream( int streamHashCode, int serverID); // 将tcp连接规还给连接池
9 void SetStreamDamaged( int streamHashCode, int serverID); // poolKey如果不易保存,则此处简单的传-1即可
10
11 event CallBackCountChangedActiveConnectionCountChanged;
12 event CallBackPoolStateChangedPoolStateChanged;
13 }
14
15 public delegate void CallBackCountChanged( int serverID, int activeConnCount);
16 public delegate void CallBackPoolStateChanged( int serverID, bool disconnected);
17
18 public enum RentStreamResult
19 {
20 Succeed,Busy,TheServiceNotExist
21 }
你可能已经发现, ITcpPool 中的所有元素在 ITcpStreamPool 接口中都可以找到对应物,只是有些方法的参数变复杂了。这主要是因为 ITcpStreamPool 接口针对的是一个FS,而 ITcpPool可能是针对一个FS也可能是一组FS。当 ITcpPool背后是一组FS时,就 需要参数ServerID来区分每一个FS。 我们知道, AS 和对应的每个 FS 之间都使用一个 Tcp 连接池通信:
所有的这些连接池需要被管理起来,
ESFramework
中的
ITcpPoolsManager
(
连接池管理器)组件实现了对多个
Tcp
连接池的管理。为了把连接池管理器和单个连接池统一起来,使它们有相同的外部接口,所以引入了
ITcpPool
接口。
这样做的好处是,在应用中直接使用
ITcpPool
接口就可以了,而不用关心这个接口背后是一个“单个连接池”(对应单个
FS
)还是由连接池管理器管理的“一组连接池”(对应多个
FS
)。而且
ITcpPoolsManager
为我们的应用进行了很多复杂的管理,比如动能服务器的调度(实现
FS
的负载均衡)、连接池的动态添加
/
移除等。这些将在下文中介绍。
下一篇文章:ESFramework介绍之(11)-- Tcp连接池管理器
上一篇文章:
ESFramework介绍之(6)―― 基于C/S的4层架构概述
转到:
ESFramework 可复用的通信框架(序)