-
Notifications
You must be signed in to change notification settings - Fork 10k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ISocketConnectionContextFactory (#34769)
- Loading branch information
Showing
9 changed files
with
231 additions
and
103 deletions.
There are no files selected for viewing
3 changes: 0 additions & 3 deletions
3
src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionContextFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Buffers; | ||
using System.IO.Pipelines; | ||
using System.Net.Sockets; | ||
using Microsoft.AspNetCore.Connections; | ||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets | ||
{ | ||
/// <summary> | ||
/// A factory for socket based connections contexts. | ||
/// </summary> | ||
public sealed class SocketConnectionContextFactory : IDisposable | ||
{ | ||
private readonly MemoryPool<byte> _memoryPool; | ||
private readonly SocketConnectionFactoryOptions _options; | ||
private readonly ISocketsTrace _trace; | ||
private readonly int _settingsCount; | ||
private readonly QueueSettings[] _settings; | ||
private int _settingsIndex; | ||
|
||
/// <summary> | ||
/// Creates the <see cref="SocketConnectionContextFactory"/>. | ||
/// </summary> | ||
/// <param name="options">The options.</param> | ||
/// <param name="logger">The logger.</param> | ||
public SocketConnectionContextFactory(SocketConnectionFactoryOptions options, ILogger logger) | ||
{ | ||
if (options == null) | ||
{ | ||
throw new ArgumentNullException(nameof(options)); | ||
} | ||
|
||
if (logger == null) | ||
{ | ||
throw new ArgumentNullException(nameof(logger)); | ||
} | ||
|
||
_options = options; | ||
_trace = new SocketsTrace(logger); | ||
_memoryPool = _options.MemoryPoolFactory(); | ||
_settingsCount = _options.IOQueueCount; | ||
|
||
var maxReadBufferSize = _options.MaxReadBufferSize ?? 0; | ||
var maxWriteBufferSize = _options.MaxWriteBufferSize ?? 0; | ||
var applicationScheduler = options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : PipeScheduler.ThreadPool; | ||
|
||
if (_settingsCount > 0) | ||
{ | ||
_settings = new QueueSettings[_settingsCount]; | ||
|
||
for (var i = 0; i < _settingsCount; i++) | ||
{ | ||
var transportScheduler = options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : new IOQueue(); | ||
// https://github.com/aspnet/KestrelHttpServer/issues/2573 | ||
var awaiterScheduler = OperatingSystem.IsWindows() ? transportScheduler : PipeScheduler.Inline; | ||
|
||
_settings[i] = new QueueSettings() | ||
{ | ||
Scheduler = transportScheduler, | ||
InputOptions = new PipeOptions(_memoryPool, applicationScheduler, transportScheduler, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false), | ||
OutputOptions = new PipeOptions(_memoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false), | ||
SocketSenderPool = new SocketSenderPool(awaiterScheduler) | ||
}; | ||
} | ||
} | ||
else | ||
{ | ||
var transportScheduler = options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : PipeScheduler.ThreadPool; | ||
// https://github.com/aspnet/KestrelHttpServer/issues/2573 | ||
var awaiterScheduler = OperatingSystem.IsWindows() ? transportScheduler : PipeScheduler.Inline; | ||
_settings = new QueueSettings[] | ||
{ | ||
new QueueSettings() | ||
{ | ||
Scheduler = transportScheduler, | ||
InputOptions = new PipeOptions(_memoryPool, applicationScheduler, transportScheduler, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false), | ||
OutputOptions = new PipeOptions(_memoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false), | ||
SocketSenderPool = new SocketSenderPool(awaiterScheduler) | ||
} | ||
}; | ||
_settingsCount = 1; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Create a <see cref="ConnectionContext"/> for a socket. | ||
/// </summary> | ||
/// <param name="socket">The socket for the connection.</param> | ||
/// <returns></returns> | ||
public ConnectionContext Create(Socket socket) | ||
{ | ||
var setting = _settings[Interlocked.Increment(ref _settingsIndex) % _settingsCount]; | ||
|
||
var connection = new SocketConnection(socket, | ||
_memoryPool, | ||
setting.Scheduler, | ||
_trace, | ||
setting.SocketSenderPool, | ||
setting.InputOptions, | ||
setting.OutputOptions, | ||
waitForData: _options.WaitForDataBeforeAllocatingBuffer); | ||
|
||
connection.Start(); | ||
return connection; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public void Dispose() | ||
{ | ||
// Dispose the memory pool | ||
_memoryPool.Dispose(); | ||
|
||
// Dispose any pooled senders | ||
foreach (var setting in _settings) | ||
{ | ||
setting.SocketSenderPool.Dispose(); | ||
} | ||
} | ||
|
||
private class QueueSettings | ||
{ | ||
public PipeScheduler Scheduler { get; init; } = default!; | ||
public PipeOptions InputOptions { get; init; } = default!; | ||
public PipeOptions OutputOptions { get; init; } = default!; | ||
public SocketSenderPool SocketSenderPool { get; init; } = default!; | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionFactoryOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Buffers; | ||
|
||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets | ||
{ | ||
/// <summary> | ||
/// Options for <see cref="SocketConnectionContextFactory"/>. | ||
/// </summary> | ||
public class SocketConnectionFactoryOptions | ||
{ | ||
/// <summary> | ||
/// Create a new instance. | ||
/// </summary> | ||
public SocketConnectionFactoryOptions() { } | ||
|
||
internal SocketConnectionFactoryOptions(SocketTransportOptions transportOptions) | ||
{ | ||
IOQueueCount = transportOptions.IOQueueCount; | ||
WaitForDataBeforeAllocatingBuffer = transportOptions.WaitForDataBeforeAllocatingBuffer; | ||
MaxReadBufferSize = transportOptions.MaxReadBufferSize; | ||
MaxWriteBufferSize = transportOptions.MaxWriteBufferSize; | ||
UnsafePreferInlineScheduling = transportOptions.UnsafePreferInlineScheduling; | ||
MemoryPoolFactory = transportOptions.MemoryPoolFactory; | ||
} | ||
|
||
/// <summary> | ||
/// The number of I/O queues used to process requests. Set to 0 to directly schedule I/O to the ThreadPool. | ||
/// </summary> | ||
/// <remarks> | ||
/// Defaults to <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16. | ||
/// </remarks> | ||
public int IOQueueCount { get; set; } = Math.Min(Environment.ProcessorCount, 16); | ||
|
||
/// <summary> | ||
/// Wait until there is data available to allocate a buffer. Setting this to false can increase throughput at the cost of increased memory usage. | ||
/// </summary> | ||
/// <remarks> | ||
/// Defaults to true. | ||
/// </remarks> | ||
public bool WaitForDataBeforeAllocatingBuffer { get; set; } = true; | ||
|
||
/// <summary> | ||
/// Gets or sets the maximum unconsumed incoming bytes the transport will buffer. | ||
/// </summary> | ||
public long? MaxReadBufferSize { get; set; } = 1024 * 1024; | ||
|
||
/// <summary> | ||
/// Gets or sets the maximum outgoing bytes the transport will buffer before applying write backpressure. | ||
/// </summary> | ||
public long? MaxWriteBufferSize { get; set; } = 64 * 1024; | ||
|
||
/// <summary> | ||
/// Inline application and transport continuations instead of dispatching to the threadpool. | ||
/// </summary> | ||
/// <remarks> | ||
/// This will run application code on the IO thread which is why this is unsafe. | ||
/// It is recommended to set the DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS environment variable to '1' when using this setting to also inline the completions | ||
/// at the runtime layer as well. | ||
/// This setting can make performance worse if there is expensive work that will end up holding onto the IO thread for longer than needed. | ||
/// Test to make sure this setting helps performance. | ||
/// </remarks> | ||
public bool UnsafePreferInlineScheduling { get; set; } | ||
|
||
internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = PinnedBlockMemoryPoolFactory.Create; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.