在托管于AWS ECS FARGATE(码头工人)的dotnet core 2.2 REST服务中,即使ECS报告最大内存使用量为11%,我也经常(每30-60分钟)使实例崩溃System.OutOfMemoryException
(超出16GB)。崩溃总是来自TaskScheduler(下面的堆栈跟踪)。它只会在生产中发生。
我正在寻求有关如何解决此问题的建议。 (编辑:我不认为这实际上是内存不足的问题,除非Thread:StartInternal()
突然可以比AWS监控工具注册速度更快地使用16GB的90%)
该应用程序可在Windows 10上本地运行,并且我还尝试通过维持100个并发请求在一个单独的ECS群集(我们的测试群集)上进行复制,但是没有运气。 服务的一个端点接收99%以上的请求。基本操作是:
async/await
System.New.WebRequest
和async/await
从外部URL(有时很慢)获取数据 WCF服务称为同步,因为我们在WCF之上使用客户端库,这不是异步安全的。但是,结果将在MemoryCache
中存储1分钟,并且使用AsyncEx.AsyncMonitor来保护过期时的重取,因此只允许一个调用方更新缓存,如下所示:
using( await _monitor.EnterAsync( ) )
{
if( !Cache.TryGetValue( "UserLookup", out LookupUsers lookupUsers ) )
{
lookupUsers = await GetCachedUsers( ssoToken );
Cache.Set( "UserLookup", lookupUsers, TimeSpan.FromMinutes( 1 ) );
}
return lookupUsers;
}
GetCachedUsers()
执行此操作:
var users = await Task.Run( ( ) => client.Proxy.ListUsers( new ListUsersInput { } ) );
并且还会在超时或其他问题时返回默认值。
动作的切入点是这样:
[Route( "get-content" )]
[HttpPost]
public async Task<RemoteGetContentResult> GetContent( [FromBody]RemoteGetContentInput input )
{
// input validation
var c = Interlocked.Increment( ref _concurrency );
try
{
// log value of _concurrency
return await _provider.GetContentExAsync( input );
}
finally
{
Interlocked.Decrement( ref _concurrency );
}
}
记录的并发级别通常为10-30,但可以达到100(当有许多外部http提取时)。
这是我在AWS ECS日志中看到的堆栈跟踪:
2019-07-10T06:22:39.554Z Unhandled Exception: System.Threading.Tasks.TaskSchedulerException: An exception was thrown by a TaskScheduler. ---> System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
2019-07-10T06:22:39.554Z at System.Threading.Thread.StartInternal()
2019-07-10T06:22:39.554Z at System.Threading.Tasks.Task.ScheduleAndStart(Boolean needsProtection)
2019-07-10T06:22:39.554Z --- End of inner exception stack trace ---
2019-07-10T06:22:39.554Z at System.Threading.Tasks.Task.ScheduleAndStart(Boolean needsProtection)
2019-07-10T06:22:39.554Z at System.Threading.Tasks.Task.InternalStartNew(Task creatingTask, Delegate action, Object state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions)
2019-07-10T06:22:39.554Z at System.Runtime.IOThreadScheduler.ScheduleCallbackHelper(SendOrPostCallback callback, Object state)
2019-07-10T06:22:39.554Z at System.Runtime.IOThreadScheduler.ScheduleCallbackNoFlow(SendOrPostCallback callback, Object state)
2019-07-10T06:22:39.554Z at System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.System.Runtime.CompilerServices.IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
2019-07-10T06:22:39.554Z at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AwaitUnsafeOnCompleted[TAwaiter,TStateMachine](TAwaiter& awaiter, TStateMachine& stateMachine)
2019-07-10T06:22:39.554Z --- End of stack trace from previous location where exception was thrown ---
2019-07-10T06:22:39.554Z at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
2019-07-10T06:22:39.554Z --- End of stack trace from previous location where exception was thrown ---
2019-07-10T06:22:39.554Z at System.Threading.ThreadPoolWorkQueue.Dispatch()
更新: 我每5秒添加一些有关此过程的其他日志记录。在18:30:16.741Z,它记录了:
2019-07-10T18:30:16.741Z concurrency: 4 proc thread cnt: 29 avail worker threads: 32,766 avail compl port threads: 1,000 ws: 1,733,996,544 peak ws: 0
因此,在16GB中,工作集约为1.7GB。 (由于某种原因,Peak WS始终为0,但是我看到的最大值是2,053,316,608字节)。 4秒后,它引发OOM异常:
2019-07-10T18:30:20.630Z Unhandled Exception: System.Threading.Tasks.TaskSchedulerException: An exception was thrown by a TaskScheduler. ---> System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
答案 0 :(得分:0)
原来,我们使用的是一个使用HttpClient
的库而没有对其进行处理,从而导致套接字泄漏。
我们在Windows上使用该库已有一段时间了,但是显然套接字最终被终结器关闭了,但是在Linux上却没有。
我终于在常规Linux机器上运行了该应用程序,从而使监视OS更加容易。原来,该命令
$ lsof -p <PID>
返回了数千行,像这样
dotnet 15613 ec2-user 215u sock 0,8 0t0 4968805 protocol: TCP
dotnet 15613 ec2-user 219u sock 0,8 0t0 4968844 protocol: TCP
dotnet 15613 ec2-user 220u sock 0,8 0t0 4968236 protocol: TCP
dotnet 15613 ec2-user 221u sock 0,8 0t0 4968247 protocol: TCP
...
将HttpClient
用法转换为单例即可解决此问题。