本文将使用一个Github开源的组件库技术来实现连接池的操作,应用于一些情况下的频繁的网络连接操作。
github地址:https://github.com/dathlin/HslCommunication 如果喜欢可以star或是fork,还可以打赏支持,打赏请认准源代码项目。
本项目目前支持C#语言和java语言,C#语言的功能比较齐全,java版本的库还在开发及完善中。
nuget地址:https://www.nuget.org/packages/HslCommunication/
github地址:https://github.com/dathlin/HslCommunication
如果喜欢可以star或是fork,还可以打赏支持。
组件的完整信息和API介绍参照:http://www.cnblogs.com/dathlin/p/7703805.html 组件的使用限制,更新日志,都在该页面里面。
为什么需要连接池
首先需要解决这个问题,我们为什么需要连接池,终极目的是是为了高效的数据访问,但并不是所有情况都需要进行连接池搭建的,举几个?吧
案例一:你有个后台线程每隔1s钟在读取PLC的数据,你和PLC的交互都在这个线程里面,那么完全是不需要进行连接池的。
案例二:你会在程序的多个线程(包括线程池里面)进行和PLC进行数据交互,频繁的读写操作,如果共用一个连接的性能达不到要求怎么办(通常在无线网络下,一次数据交互在30ms-100ms之间,网络波动较大)?,如果每次操作都创建连接,操作结束后关闭,这样也可以达到功能,只是损耗性能比较严重,无论是对PLC还是本机,通讯的效率也不是很理想,因为每次操作都是重新连接和关闭,这里的PLC实际上可以替换成数据库,Redis,其他任何的数据通信。
什么时候不能连接池
并不是所有的情况都可以使用连接池的,有个巨大的限制,如果服务器不支持多连接就很麻烦,比如说三菱PLC的服务器的端口,当然,一般的数据库,Redis,特殊的服务器都是支持。当然,有些特殊的服务器,支持的连接数是有上限的,没事,本组件也可以配置上限的连接数。
特性支持
本功能类的设计之初,就是兼顾灵活性,为了支持其他所有的不同类型的数据通信,采用接口+泛型来实现,先构建一个通信封装类,再进行创建一个连接池管理器,再调用使用。
- 支持配置最大的连接数
- 支持设置连接过期时间
- 支持任意的其他类型的连接对象变成连接池
实战示例
此处举例访问西门子的PLC,所以第一步是创建一个连接的封装类,除了实现接口外,还需要定义真实的连接对象。
public class SiemensConnector : HslCommunication.Algorithms.ConnectPool.IConnector
{
#region 构造方法
public SiemensConnector( string ipAddress )
{
siemens = new HslCommunication.Profinet.Siemens.SiemensS7Net( HslCommunication.Profinet.Siemens.SiemensPLCS.S1200, ipAddress );
}
#endregion
#region IConnector 实现
/// <summary>
/// 指示当前的连接是否在使用用
/// </summary>
public bool IsConnectUsing { get; set; }
/// <summary>
/// 唯一的GUID码
/// </summary>
public string GuidToken { get; set; }
/// <summary>
/// 最新一次使用的时间
/// </summary>
public DateTime LastUseTime { get; set; }
/// <summary>
/// 打开连接
/// </summary>
public void Open( )
{
// 设置常连接。如果是Redis,可以连接服务器,数据库也是一样
siemens.ConnectServer( );
}
/// <summary>
/// 关闭并释放
/// </summary>
public void Close( )
{
// 关闭连接
siemens.ConnectClose( );
}
#endregion
#region Public Member
/// <summary>
/// 获取当前的连接对象,方便进行数据交互
/// </summary>
/// <returns></returns>
public HslCommunication.Profinet.Siemens.SiemensS7Net GetSiemens( )
{
return siemens;
}
#endregion
#region Private Member
private HslCommunication.Profinet.Siemens.SiemensS7Net siemens; // 连接对象
#endregion
}
定义好之后,就可以创建真正的连接池对象了
private HslCommunication.Algorithms.ConnectPool.ConnectPool<SiemensConnector> siemensConnect = null; // 西门子对象的连接池
然后初始化变量
siemensConnect = new HslCommunication.Algorithms.ConnectPool.ConnectPool<SiemensConnector>( ( ) => { return new SiemensConnector( "192.168.1.195" ); } );
siemensConnect.MaxConnector = 10; // 同时存在的最大连接数
siemensConnect.ConectionExpireTime = 30; // 连接多久不用就自动回收释放,单位秒
初始化的时候,有个地方需要注意,连接池需要知道一个信息,如何去创建一个新的连接对象,在这里可能创建默认的SiemesConnector就可以类,但是连接池的管理对象很有可能也是个接口,这时候就需要手动指示如何实例化一个新的对象。这种情况我在使用dapper的ORM的时候,支持mysql和sqlserver两种数据的时候就碰到过。
调用连接对象类,以下的代码可以出现在任何的后台线程:
// 这里的代码在单线程程序情况下,没有什么效果,但是在多线程情况下可以显著提升性能。
// 举例,此处要访问PLC的一个数据
SiemensConnector connector = siemensConnect.GetAvailableConnector( );
short m100 = connector.GetSiemens( ).ReadInt16( "M100" ).Content;
// 使用完毕后归还连接
siemensConnect.ReturnConnector( connector );
使用完后务必归还连接对象,如果想要获取连接池里已经被激活的连接数
int online = siemensConnect.UsedConnector;
还有,在获取连接对象后,进行操作的时候,务必不要抛出异常,