Merge branch 'khellang/configurable-thread-count' into dev

This commit is contained in:
Stephen Halter 2015-12-08 00:13:56 -08:00
commit 90ece575f7
3 changed files with 154 additions and 29 deletions

View File

@ -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<string> 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<string> GetAddresses(IConfiguration configuration)
{
var addresses = new List<string>();
@ -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;
}
}
}

View File

@ -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<IKestrelServerInformation>(information);
serverFeatures.Set<IServerAddressesFeature>(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;
}
}
}

View File

@ -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<string, string>
{
{ "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<string> { "http://localhost:1337", "https://localhost:42" };
var values = new Dictionary<string, string>
{
{ "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<string, string>
{
{ "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;
}
}
}