diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs index ec31f4fbf6..3808b6144b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using Microsoft.AspNet.Server.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.Extensions.Configuration; @@ -11,11 +12,11 @@ namespace Microsoft.AspNet.Server.Kestrel { public class KestrelServerInformation : IKestrelServerInformation, IServerAddressesFeature { - public KestrelServerInformation(IConfiguration configuration, int threadCount) + public KestrelServerInformation(IConfiguration configuration) { Addresses = GetAddresses(configuration); - ThreadCount = threadCount; - NoDelay = true; + ThreadCount = GetThreadCount(configuration); + NoDelay = GetNoDelay(configuration); } public ICollection Addresses { get; } @@ -26,6 +27,32 @@ namespace Microsoft.AspNet.Server.Kestrel public IConnectionFilter ConnectionFilter { get; set; } + private static int ProcessorThreadCount + { + get + { + // Actual core count would be a better number + // rather than logical cores which includes hyper-threaded cores. + // Divide by 2 for hyper-threading, and good defaults (still need threads to do webserving). + var threadCount = Environment.ProcessorCount >> 1; + + if (threadCount < 1) + { + // Ensure shifted value is at least one + return 1; + } + + if (threadCount > 16) + { + // Receive Side Scaling RSS Processor count currently maxes out at 16 + // would be better to check the NIC's current hardware queues; but xplat... + return 16; + } + + return threadCount; + } + } + private static ICollection GetAddresses(IConfiguration configuration) { var addresses = new List(); @@ -39,5 +66,41 @@ namespace Microsoft.AspNet.Server.Kestrel return addresses; } + + private static int GetThreadCount(IConfiguration configuration) + { + var threadCountString = configuration["kestrel.threadCount"]; + + if (string.IsNullOrEmpty(threadCountString)) + { + return ProcessorThreadCount; + } + + int threadCount; + if (int.TryParse(threadCountString, NumberStyles.Integer, CultureInfo.InvariantCulture, out threadCount)) + { + return threadCount; + } + + return ProcessorThreadCount; + } + + private static bool GetNoDelay(IConfiguration configuration) + { + var noDelayString = configuration["kestrel.noDelay"]; + + if (string.IsNullOrEmpty(noDelayString)) + { + return true; + } + + bool noDelay; + if (bool.TryParse(noDelayString, out noDelay)) + { + return noDelay; + } + + return true; + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs index 284823b849..d7f95ce25c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs @@ -27,36 +27,11 @@ namespace Microsoft.AspNet.Server.Kestrel public IServer CreateServer(IConfiguration configuration) { - var threadCount = GetThreadCount(); - var information = new KestrelServerInformation(configuration, threadCount); + var information = new KestrelServerInformation(configuration); var serverFeatures = new FeatureCollection(); serverFeatures.Set(information); serverFeatures.Set(information); return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNet.Server.Kestrel")); } - - private static int GetThreadCount() - { - // Actual core count would be a better number - // rather than logical cores which includes hyper-threaded cores. - // Divide by 2 for hyper-threading, and good defaults (still need threads to do webserving). - // Can be user overriden using IKestrelServerInformation.ThreadCount - var threadCount = Environment.ProcessorCount >> 1; - - if (threadCount < 1) - { - // Ensure shifted value is at least one - return 1; - } - - if (threadCount > 16) - { - // Receive Side Scaling RSS Processor count currently maxes out at 16 - // would be better to check the NIC's current hardware queues; but xplat... - return 16; - } - - return threadCount; - } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/KestrelServerInformationTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/KestrelServerInformationTests.cs new file mode 100644 index 0000000000..1c56258554 --- /dev/null +++ b/test/Microsoft.AspNet.Server.KestrelTests/KestrelServerInformationTests.cs @@ -0,0 +1,87 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Server.Kestrel; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Microsoft.AspNet.Server.KestrelTests +{ + public class KestrelServerInformationTests + { + [Fact] + public void SetThreadCountUsingConfiguration() + { + const int expected = 42; + + var values = new Dictionary + { + { "kestrel.threadCount", expected.ToString() } + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); + + var information = new KestrelServerInformation(configuration); + + Assert.Equal(expected, information.ThreadCount); + } + + [Fact] + public void SetThreadCountUsingProcessorCount() + { + // Ideally we'd mock Environment.ProcessorCount to test edge cases. + var expected = Clamp(Environment.ProcessorCount >> 1, 1, 16); + + var configuration = new ConfigurationBuilder().Build(); + + var information = new KestrelServerInformation(configuration); + + Assert.Equal(expected, information.ThreadCount); + } + + [Fact] + public void SetAddressesUsingConfiguration() + { + var expected = new List { "http://localhost:1337", "https://localhost:42" }; + + var values = new Dictionary + { + { "server.urls", string.Join(";", expected) } + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); + + var information = new KestrelServerInformation(configuration); + + Assert.Equal(expected, information.Addresses); + } + + [Fact] + public void SetNoDelayUsingConfiguration() + { + var values = new Dictionary + { + { "kestrel.noDelay", "false" } + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); + + var information = new KestrelServerInformation(configuration); + + Assert.False(information.NoDelay); + } + + private static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } + } +} \ No newline at end of file