HubbleDotNet 1.1.2 以前版本客户端和服务器之间都是同步 TCP Socket 方式通讯,这种方式要为每个连接保持一个TCP通道,每次建立连接都要创建新的TCP连接,效率很低。而且有最大连接数限制,当服务器很忙时,1.1.2以前版本经常会出现 Too many connects on server 错误,虽然通过增大 MaxConnectNum 的值可以缓解,但无法根治。1.1.2 版本推出了全新的异步通讯模式,同一个客户端和服务器之间所有的 Hubbble 连接都共用一个 TCP 链路,这个设计彻底解决了最大连接数不够的问题,大大提高了系统的处理能力。
适用版本
V1.1.2 以上版本。客户端和服务器都需要升级到这个版本以上。升级方法
异步通讯原理
HubbleDotNet 全新的异步通讯模式采用类似虚通道的设计方法。
如上图所示,Client 和 Server 之间始终保持一个物理TCP 链路,不同的 HubbleAsyncConnection 连接被分配了不同的虚通道id,在这个物理TCP链路中异步传输,对于客户端这边,在执行 Query 方法时,感觉上还是同步过程,就是服务器返回结果后,Query 方法才返回,但底层则是通过异步实现的。其实现过程如下:
Step1: 客户端分配虚通道id,并发送异步查询请求给服务器,然后等待一个 Event 对象
Step2: 服务器收到这个异步查询请求后,为这个请求分配空闲的工作线程处理
Step3: 工作线程处理完毕,服务器向客户端回送异步消息返回结果
Step4: 客户端根据服务器返回的虚通道id,找到对应的 HubbleAsyncConnection,将结果返回给 HubbleAsyncConnection,并设置Event 事件
Step5: Query 方法结束等待,返回结果给调用者。
采取异步通讯机制后,如下功能发生了一些变化:
1. HubbleCommand 的 CommandTimeout 可以起到作用,以前是没有用的。当 CommandTimeout 到时,会触发超时异常。
2. 服务器侧TableInfo 中的 Select timout 机制生效时的结果发生了不同
如上图所示: Select timeout 是 1.1 版本的一个新功能,这个功能的作用是设置 select 语句的执行超时,默认为-1,表示超时时间无限大,直到执行完才返回。
如果设置了大于0的值,当 select 语句执行超过这个超时时间后,服务器将自动终止这个操作。释放资源给其他的查询。这个功能主要在服务器负荷很高时发挥作用,将执行时间过长的查询终止掉,以释放系统资源给其他的查询请求。
如果采用 HubbleConnection 连接(即同步方式连接) ,当Select timout 超时后,服务器将终止掉当前TCP连接,客户端得到一个 Tcp 连接中断的错误,这个错误不容易判到底是发生了什么,只有查询服务器侧的日志才能知道。
如果采用 HubbleAsyncConnection 连接(即异步方式连接) , 当Select timout 超时后,服务器不会终止当前TCP连接,而是向对应虚通道发送一个特殊的错误,通过这个错误,客户端可以比较容易判断当前到底是出现了什么情况。
异步通讯方式的调用方法
异步通讯方式的调用方法和同步方式完全一样,只要把HubbleConnection 替换成 HubbleAsyncConnection 就可以了。
using (HubbleAsyncConnection conn =
new HubbleAsyncConnection("Data Source=127.0.0.1;Initial Catalog=test;"))
{
conn.Open();
try { HubbleCommand command =
new HubbleCommand("select id from News where docid = 1", conn);
System.Data.DataSet qResult = command.Query();
}
catch (ServerException se)
{
Console.WriteLine(se.Message);
Console.WriteLine(se.InnerStackTrace);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}
上图为简单的异步调用过程,可以看出除了把 HubbleConnection 替换成 HubbleAsyncConnection ,其他和同步调用是一样的。
如果要把现有系统的通讯方式改成异步方式,方法也是一样的,只要简单替换HubbleConnection为HubbleAsyncConnection就可以了,非常简单。
异步方式和同步方式的执行效率对比
同步连接方式测试代码
for (int j = 0; j < 10000; j++)
{
using (HubbleConnection conn =
new HubbleConnection("Data Source=127.0.0.1;Initial Catalog=test;"))
{
conn.Open();
try { HubbleCommand command = new HubbleCommand("select id from News where docid = 1", conn);
System.Data.DataSet qResult = command.Query();
}
catch (ServerException se)
{
Console.WriteLine(se.Message);
Console.WriteLine(se.InnerStackTrace);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}
}
异步连接方式测试代码
for (int j = 0; j < 10000; j++)
{
using (HubbleAsyncConnection conn =
new HubbleAsyncConnection("Data Source=127.0.0.1;Initial Catalog=test;"))
{
conn.Open();
try { HubbleCommand command = new HubbleCommand("select id from News where docid = 1", conn);
System.Data.DataSet qResult = command.Query();
}
catch (ServerException se)
{
Console.WriteLine(se.Message);
Console.WriteLine(se.InnerStackTrace);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}
}
测试结果
同步调用执行1万次用时47秒,平均每秒执行212次
异步调用执行1万次用时4秒,平均每秒执行2500次
异步调用方式在通讯速度上大概比同步方式快10倍以上。