Abort connections not closed during shutdown (#1112).

This commit is contained in:
Cesar Blum Silveira 2016-12-14 16:53:19 -08:00
parent 8b44e8e382
commit b41c4078bd
7 changed files with 50 additions and 13 deletions

View File

@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
return _socketClosedTcs.Task; return _socketClosedTcs.Task;
} }
public virtual void Abort(Exception error = null) public virtual Task AbortAsync(Exception error = null)
{ {
// Frame.Abort calls user code while this method is always // Frame.Abort calls user code while this method is always
// called from a libuv thread. // called from a libuv thread.
@ -150,6 +150,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{ {
_frame.Abort(error); _frame.Abort(error);
}); });
return _socketClosedTcs.Task;
} }
// Called on Libuv thread // Called on Libuv thread
@ -285,7 +287,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (!normalRead) if (!normalRead)
{ {
Abort(error); AbortAsync(error);
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking; using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
@ -22,17 +21,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
} }
public async Task<bool> WalkConnectionsAndCloseAsync(TimeSpan timeout) public async Task<bool> WalkConnectionsAndCloseAsync(TimeSpan timeout)
{
return await WalkConnectionsAsync((connectionManager, tcs) => connectionManager.WalkConnectionsAndCloseCore(tcs), timeout).ConfigureAwait(false);
}
public async Task<bool> WalkConnectionsAndAbortAsync(TimeSpan timeout)
{
return await WalkConnectionsAsync((connectionManager, tcs) => connectionManager.WalkConnectionsAndAbortCore(tcs), timeout).ConfigureAwait(false);
}
private async Task<bool> WalkConnectionsAsync(Action<ConnectionManager, TaskCompletionSource<object>> action, TimeSpan timeout)
{ {
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object>();
_thread.Post(state => ((ConnectionManager)state).WalkConnectionsAndCloseCore(tcs), this); _thread.Post(state => action((ConnectionManager)state, tcs), this);
return await Task.WhenAny(tcs.Task, Task.Delay(timeout)).ConfigureAwait(false) == tcs.Task; return await Task.WhenAny(tcs.Task, Task.Delay(timeout)).ConfigureAwait(false) == tcs.Task;
} }
private void WalkConnectionsAndCloseCore(TaskCompletionSource<object> tcs) private void WalkConnectionsAndCloseCore(TaskCompletionSource<object> tcs)
{ {
var connectionStopTasks = new List<Task>(); WalkConnectionsCore(connection => connection.StopAsync(), tcs);
}
private void WalkConnectionsAndAbortCore(TaskCompletionSource<object> tcs)
{
WalkConnectionsCore(connection => connection.AbortAsync(), tcs);
}
private void WalkConnectionsCore(Func<Connection, Task> action, TaskCompletionSource<object> tcs)
{
var tasks = new List<Task>();
_thread.Walk(ptr => _thread.Walk(ptr =>
{ {
@ -41,13 +60,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (connection != null) if (connection != null)
{ {
connectionStopTasks.Add(connection.StopAsync()); tasks.Add(action(connection));
} }
}); });
_threadPool.Run(() => _threadPool.Run(() =>
{ {
Task.WaitAll(connectionStopTasks.ToArray()); Task.WaitAll(tasks.ToArray());
tcs.SetResult(null); tcs.SetResult(null);
}); });
} }

View File

@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
_connection.Abort(); _connection.AbortAsync();
_cancelled = true; _cancelled = true;
return TaskUtilities.GetCancelledTask(cancellationToken); return TaskUtilities.GetCancelledTask(cancellationToken);
} }
@ -304,7 +304,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{ {
// Abort the connection for any failed write // Abort the connection for any failed write
// Queued on threadpool so get it in as first op. // Queued on threadpool so get it in as first op.
_connection.Abort(); _connection.AbortAsync();
_cancelled = true; _cancelled = true;
CompleteAllWrites(); CompleteAllWrites();
@ -374,7 +374,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{ {
// Abort the connection for any failed write // Abort the connection for any failed write
// Queued on threadpool so get it in as first op. // Queued on threadpool so get it in as first op.
_connection.Abort(); _connection.AbortAsync();
_cancelled = true; _cancelled = true;
_lastWriteError = error; _lastWriteError = error;
} }
@ -532,7 +532,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
_connection.Abort(); _connection.AbortAsync();
_cancelled = true; _cancelled = true;
return TaskUtilities.GetCancelledTask(cancellationToken); return TaskUtilities.GetCancelledTask(cancellationToken);
} }

View File

@ -43,6 +43,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
void NotAllConnectionsClosedGracefully(); void NotAllConnectionsClosedGracefully();
void NotAllConnectionsAborted();
void ApplicationError(string connectionId, Exception ex); void ApplicationError(string connectionId, Exception ex);
} }
} }

View File

@ -165,6 +165,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
if (!await ConnectionManager.WalkConnectionsAndCloseAsync(_shutdownTimeout).ConfigureAwait(false)) if (!await ConnectionManager.WalkConnectionsAndCloseAsync(_shutdownTimeout).ConfigureAwait(false))
{ {
_log.NotAllConnectionsClosedGracefully(); _log.NotAllConnectionsClosedGracefully();
if (!await ConnectionManager.WalkConnectionsAndAbortAsync(TimeSpan.FromSeconds(1)).ConfigureAwait(false))
{
_log.NotAllConnectionsAborted();
}
} }
var result = await WaitAsync(PostAsync(state => var result = await WaitAsync(PostAsync(state =>
@ -281,7 +286,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
{ {
lock (_startSync) lock (_startSync)
{ {
var tcs = (TaskCompletionSource<int>) parameter; var tcs = (TaskCompletionSource<int>)parameter;
try try
{ {
_loop.Init(_engine.Libuv); _loop.Init(_engine.Libuv);

View File

@ -26,6 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
private static readonly Action<ILogger, string, int, Exception> _connectionDisconnectedWrite; private static readonly Action<ILogger, string, int, Exception> _connectionDisconnectedWrite;
private static readonly Action<ILogger, string, long, Exception> _connectionHeadResponseBodyWrite; private static readonly Action<ILogger, string, long, Exception> _connectionHeadResponseBodyWrite;
private static readonly Action<ILogger, Exception> _notAllConnectionsClosedGracefully; private static readonly Action<ILogger, Exception> _notAllConnectionsClosedGracefully;
private static readonly Action<ILogger, Exception> _notAllConnectionsAborted;
private static readonly Action<ILogger, string, string, Exception> _connectionBadRequest; private static readonly Action<ILogger, string, string, Exception> _connectionBadRequest;
private static readonly Action<ILogger, string, Exception> _connectionReset; private static readonly Action<ILogger, string, Exception> _connectionReset;
private static readonly Action<ILogger, string, Exception> _requestProcessingError; private static readonly Action<ILogger, string, Exception> _requestProcessingError;
@ -54,6 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
_connectionHeadResponseBodyWrite = LoggerMessage.Define<string, long>(LogLevel.Debug, 18, @"Connection id ""{ConnectionId}"" write of ""{count}"" body bytes to non-body HEAD response."); _connectionHeadResponseBodyWrite = LoggerMessage.Define<string, long>(LogLevel.Debug, 18, @"Connection id ""{ConnectionId}"" write of ""{count}"" body bytes to non-body HEAD response.");
_connectionReset = LoggerMessage.Define<string>(LogLevel.Debug, 19, @"Connection id ""{ConnectionId}"" reset."); _connectionReset = LoggerMessage.Define<string>(LogLevel.Debug, 19, @"Connection id ""{ConnectionId}"" reset.");
_requestProcessingError = LoggerMessage.Define<string>(LogLevel.Information, 20, @"Connection id ""{ConnectionId}"" request processing ended abnormally."); _requestProcessingError = LoggerMessage.Define<string>(LogLevel.Information, 20, @"Connection id ""{ConnectionId}"" request processing ended abnormally.");
_notAllConnectionsAborted = LoggerMessage.Define(LogLevel.Debug, 21, "Some connections failed to abort during server shutdown.");
} }
public KestrelTrace(ILogger logger) public KestrelTrace(ILogger logger)
@ -149,6 +151,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
_notAllConnectionsClosedGracefully(_logger, null); _notAllConnectionsClosedGracefully(_logger, null);
} }
public virtual void NotAllConnectionsAborted()
{
_notAllConnectionsAborted(_logger, null);
}
public void ConnectionBadRequest(string connectionId, BadHttpRequestException ex) public void ConnectionBadRequest(string connectionId, BadHttpRequestException ex)
{ {
_connectionBadRequest(_logger, connectionId, ex.Message, ex); _connectionBadRequest(_logger, connectionId, ex.Message, ex);

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel; using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Internal; using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Testing namespace Microsoft.AspNetCore.Testing
{ {
@ -24,9 +25,10 @@ namespace Microsoft.AspNetCore.Testing
}; };
} }
public override void Abort(Exception error = null) public override Task AbortAsync(Exception error = null)
{ {
RequestAbortedSource?.Cancel(); RequestAbortedSource?.Cancel();
return TaskCache.CompletedTask;
} }
public override void OnSocketClosed() public override void OnSocketClosed()