在我们的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 ;(
),但在发生上述问题时不会发生这种情况。
非常感谢任何建议或建议。
答案 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文章所示: