SqlException: A transport-level error has occurred when receiving results from the server.
🏷️ SQL Server
现象
新项目使用了 .NET Core 来开发,上线到生产后会不定时的报如下 SqlException:
A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - Success)
生产的数据库(SqlServer)采用的一写多读的配置,读库采用 HAProxy 实现负载均衡。
在 HAProxy 中设置了数据库连接的超时时间(50s),超过该时间则将数据库连接关闭。
写库由于是单台,采用的是直连。
对策
延长读库 HAProxy 的连接超时时间(之前是 50s 修改为 5min)。过去两天了,没有再出现这个异常。
原因(猜测)
由于使用的 SqlConeection
,默认是开启数据库连接池的,当连接使用结束后并没有将其关闭,而是放回到数据库连接池。
当该连接超过 50s(从该连接被创建开始),HAProxy 将该连接关闭,但数据库连接池中仍然存在这个 SqlConeection
,并且认为其是有效的。
当其从池拿出来使用时,就发生了该异常。
过期时间延长到 5min 之后本以为只是出现错误的几率变低,没想到结果没有再出错。
这个可能是因为数据库连接池也会定期的移除空闲连接的原因。
参考官方的文档,这个时间是 4-8 分钟。
此处仍然有疑问,这个 4-8 分钟是连接空闲的时间,如果某个连接被反复的重用,是很有可能存活超过 5min 的。这个嘛,我觉得是由于请求量并没有那么高,导致还没有发生过这种情况(毕竟新项目,功能还很少)。
另外有个非常不解的地方:之前生产环境中的项目(采用的 .NET Freamwork 4.5)就没有该问题。
同样也是默认开启的数据库连接池,相同的数据库,相同的环境,代码也是类似的(也是用的 SqlConeection
)。
如果是上面的原因的话,应该也会有这个异常发生才对。
之前 Java 的项目采用数据库连接池后出现过 数据库连接已关闭 的问题(见 【Java】Tomcat 的 JDBC 连接池),虽然错误消息不一样,但我觉得很有可能也是这个原因导致的。
参考
移除连接
如果空闲时间达到大约 4-8 分钟,或池进程检测到与服务器的连接已断开,连接池进程会将该连接从池中移除。注意,只有在尝试与服务器进行通信之后才能检测到断开的连接。如果发现某连接不再连接到服务器,则会将其标记为无效。无效连接只有在关闭或重新建立后,才会从连接池中移除。
如果存在一个与已消失的服务器的连接,即使连接池进程尚未检测到断开的连接,也可以从池中取出此连接并将连接标记为无效。这种情况是因为检查连接是否仍有效的系统开销将造成与服务器的另一次往返,从而抵消了池进程的优势。发生此情况时,初次尝试使用该连接将检测连接是否曾断开,并引发异常。A transport-level error has occurred when receiving results from the server [closed]