db.SaveChanges()返回'远程主机强行关闭现有连接。

时间:2018-02-23 09:40:07

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

在我们的Web应用程序(在Azure上运行)中,用户上传了多个XML文件。这些文件保存在Azure云存储中,并且记录将插入表中。 我们还有一个正在查看该表的Web作业(用C#编写),当插入新记录时,它会从存储中下载XML文件,解析它并将数据保存到多个表中。 这运行良好超过2年。最近我们的客户正在上传更大的XML文件(300多MB),这需要更长的时间来处理,我们遇到了time-out errors。增加超时后,我们得到了Out-of-Memory errors。 我们现在正在使用BulkSaveChanges中的ZZProjects而我们不再获得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上运行。

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

我已经明白这是一个非常普遍的错误。如果我可以获得有关抛出此错误的原因的更多详细信息,那将是很好的。但我无法再在Azure上找到日志。

我保存了一个可能有多个子对象的对象,这使得分割对象和保存较小的部分非常困难。

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

非常感谢任何建议或建议。

2 个答案:

答案 0 :(得分: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行。

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

希望这有助于其他人。花了我们几周;)

答案 1 :(得分:0)

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