【问题标题】:db.SaveChanges() returns 'An existing connection was forcibly closed by the remote host.'db.SaveChanges() 返回“现有连接被远程主机强行关闭。”
【发布时间】:2018-03-23 16:01:22
【问题描述】:

在我们的 Web 应用程序(在 Azure 上运行)中,用户上传了几个 XML 文件。这些文件保存在 Azure 云存储中,并在表中插入一条记录。 我们还有一个 Web 作业(用 C# 编写)正在查看该表,当插入新记录时,它会从存储中下载 XML 文件,对其进行解析并将数据保存到多个表中。 这运行良好超过 2 年。最近我们的客户正在上传更大的 XML 文件 (300+ MB),处理时间更长,我们遇到了time-out errors。增加超时后,我们得到了Out-of-Memory errors。 我们现在使用来自ZZProjectsBulkSaveChanges,我们不再使用OOM-exceptions,作为奖励,现在节省大约需要17 分钟而不是3 多个小时。

对于一些(大约 10%)的大文件,我们会遇到以下类型的错误:

2018-02-23 01:19:06,993 [1] DEBUG Services.DeclarationService - Ready to save to the db
2018-02-23 01:21:10,778 [1] ERROR DataAccess.ApplicationUoW - 
Error in saving data: A transport-level error has occurred when sending the request to the server. 
  (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
System.Data.SqlClient.SqlException (0x80131904): A transport-level error has occurred when sending the request to the server. 
  (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.) 
   ---> System.ComponentModel.Win32Exception (0x80004005): An existing connection was forcibly closed by the remote host
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParserStateObject.SNIWritePacket(SNIHandle handle, SNIPacket packet, UInt32& sniError, Boolean canAccumulate, Boolean callerHasConnectionLock)
   at System.Data.SqlClient.TdsParserStateObject.WriteSni(Boolean canAccumulate)
   at System.Data.SqlClient.TdsParserStateObject.WritePacket(Byte flushMode, Boolean canAccumulate)
   at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName, Boolean shouldReconnect)
   at System.Data.SqlClient.SqlConnection.BeginTransaction(IsolationLevel iso, String transactionName)
   at System.Data.SqlClient.SqlConnection.BeginDbTransaction(IsolationLevel isolationLevel)
   at System.Data.Common.DbConnection.BeginTransaction()
   at DbContextExtensions..(BulkOperation )
   at Z.EntityFramework.Extensions.EntityBulkOperation`1.(Action`1 )
   at DbContextExtensions.[](DbContext , Action`1 )
   at DbContextExtensions.[](DbContext , IEnumerable`1 , Action`1 , List`1 )
   at Z.EntityFramework.Extensions.BulkSaveChanges.(DbContext , List`1 , List`1 , Action`1 )
   at DbContextExtensions.(DbContext , Boolean , Action`1 , Boolean )
ClientConnectionId:####
Error Number:10054,State:0,Class:20
ClientConnectionId before routing:####
Routing Destination:####,11003

我已经在互联网上搜索了几天,我看到了类似的问题,但它们主要与 MSSMS 等客户端软件有关,并通过重新打开客户端软件来解决。我没有在本地使用客户端软件。一切都在 Azure 上运行。

我们的网络应用程序中确实有一个手动重试机制,当我重试失败的 XML 文件时,它一直失败。但是当我在本地运行我的网络作业(它只是一个.exe)但仍然连接到我的 Azure SQL 数据库和 Azure 存储时,失败的 XML 文件确实得到了处理。

我已经明白这是一个非常普遍的错误。如果我能获得有关引发此错误的原因的更多详细信息,那就太好了。但我在 Azure 上找不到更多日志记录。

我正在保存一个可能有多个子对象的对象,因此很难拆分对象并保存较小的部分。

虽然 Azure 每周会重新启动几次 (very nice on a production environment ;(),但在出现上述问题时不会发生这种情况。

任何建议或意见将不胜感激。

【问题讨论】:

标签: azure tcp azure-sql-database azure-webjobs


【解决方案1】:

感谢 Azure 支持,问题得以解决。在我的错误日志中,列出了 connectionId,因此他们可以非常仔细地查看它。他们可以看到连接被打开,一些数据被检索,然后连接空闲超过 30 分钟。因此,Azure 会关闭连接。

在这个空闲时刻,我们的应用程序正在做一些事情,而对于大文件,这需要 30 多分钟。 Azure 工程师建议我们应该添加一个“KeepAlive”方法并每 5 分钟调用一次以保持连接打开。

在我们的工作单元类中,我添加了private readonly Timer _timer = new Timer(5 * 60 * 1000); 及其构造函数:

_timer.Elapsed += (sender, e) => KeepAlive();
_timer.Start();

我们的 KeepAlive 方法只是获取一个小表的行:

private void KeepAlive()
{
    var dummy = _context.BankAccounts.AsNoTracking().ToList().Count;
    Console.WriteLine($@"{DateTime.Now} KeepAlive");
}

我们可以使用.Take(1) 来增强这一点,只获得 1 行。

添加后,我们终于可以完成我们的大文件了。

希望这对其他人有所帮助。我们花了几个星期;)

【讨论】:

  • 您需要将连接打开 30 分钟的唯一原因是,如果您还将事务保持打开 30 分钟。否则,您应该尽可能晚地打开连接并尽可能早地关闭它。连接不应该有 30 秒的空闲时间,并且默认不是 30 分钟
【解决方案2】:

关于处理那些大型 XML 文件,您是否考虑过在处理它们之前将它们拆分?由于在文件较小时您从未遇到过问题,因此您可以考虑在将这些文件上传到 Azure 存储之前在客户端拆分这些文件,如this 文章所示:

【讨论】:

  • 谢谢阿尔贝托。我们确实考虑过拆分文件,但由于文件的内容,这使得它变得更加复杂。我现在正在研究通过在我的工作单元中每 5 分钟进行一次简单的选择来保持连接处于活动状态。昨晚它确实成功完成了 2 个文件,但在那之后就停止了。我做了一个小的调整,该过程再次运行。我会告诉你是否成功。
猜你喜欢
  • 2021-05-12
相关资源
最近更新 更多