当客户端终止连接时,完成端口线程正在泄漏
completion port thread is leaking when client terminates the connection
我有一个 ASP.NET WebApi 自托管应用程序。 HttpSelfHostConfiguration
配置如下
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://0.0.0.0:54781")
{
TransferMode = TransferMode.StreamedResponse,
MaxConcurrentRequests = 1000,
SendTimeout = TimeSpan.FromMinutes(60),
};
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}",
defaults: new { @controller = "Default" }
);
HttpSelfHostServer server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
for (;;)
{
int workerThreads, completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("Available Completion Port Threads = {0};", completionPortThreads);
Console.Out.Flush();
Thread.Sleep(2000);
}
有一个接受 HTTP GET 请求的操作如下
public class DefaultController : ApiController
{
public HttpResponseMessage GET()
{
Console.WriteLine("Receive HTTP GET request");
Func<Stream, HttpContent, TransportContext, Task> func = (stream, httpContent, transportContext) =>
{
return Monitor(httpContent, stream);
};
HttpResponseMessage response = Request.CreateResponse();
response.StatusCode = HttpStatusCode.OK;
response.Content = new PushStreamContent(func, new MediaTypeHeaderValue("text/plain"));
return response;
}
async Task Monitor(HttpContent httpContent, Stream stream)
{
try
{
using (StreamWriter sw = new StreamWriter(stream, new UTF8Encoding(false)))
{
for (;;)
{
sw.WriteLine(Guid.NewGuid().ToString());
sw.Flush();
await Task.Delay(1000);
}
}
}
catch (CommunicationException ce)
{
HttpListenerException ex = ce.InnerException as HttpListenerException;
if (ex != null)
{
Console.WriteLine("HttpListenerException occurs, error code = {0}", ex.ErrorCode);
}
else
{
Console.WriteLine("{0} occurs : {1}", ce.GetType().Name, ce.Message);
}
}
catch (Exception ex)
{
Console.WriteLine("{0} occurs : {1}", ex.GetType().Name, ex.Message);
}
finally
{
stream.Close();
stream.Dispose();
httpContent.Dispose();
Console.WriteLine("Dispose");
}
}
}
打开 url http://127.0.0.1:54781/
,我看到渐进式响应即将到来。
但是如果客户端在服务端发送响应时终止连接,完成端口线程会被占用,永远不会释放。
它能够通过耗尽完成端口线程池来关闭应用程序。
改成OWIN自托管后,这个问题就消失了。这似乎是 System.Web.Http.SelfHost
中的错误
这是更新后的代码:
class Program
{
static void Main(string[] args)
{
var server = WebApp.Start<Startup>(url: "http://*:54781");
for (;;)
{
int workerThreads, completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("Worker Threads = {0}; Available Completion Port Threads = {1};", workerThreads, completionPortThreads);
Console.Out.Flush();
Thread.Sleep(2000);
}
}
}
public class Startup
{
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}",
defaults: new { @controller = "Default" }
);
appBuilder.UseWebApi(config);
}
}
对于其他人来说 post System.Web.Http.SelfHost
中也有类似的问题,默认情况下使用 CPU 核心 * 100 个线程,这与默认的 ThreadPool 限制相同,结尾为Mono pre-creates 所有线程的死锁。
解决此问题的最简单方法是将 MONO_THREADS_PER_CPU
环境变量设置为大于 1 的值,或者在 运行 Mono 应用程序时使用 --server
标志。
我有一个 ASP.NET WebApi 自托管应用程序。 HttpSelfHostConfiguration
配置如下
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://0.0.0.0:54781")
{
TransferMode = TransferMode.StreamedResponse,
MaxConcurrentRequests = 1000,
SendTimeout = TimeSpan.FromMinutes(60),
};
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}",
defaults: new { @controller = "Default" }
);
HttpSelfHostServer server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
for (;;)
{
int workerThreads, completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("Available Completion Port Threads = {0};", completionPortThreads);
Console.Out.Flush();
Thread.Sleep(2000);
}
有一个接受 HTTP GET 请求的操作如下
public class DefaultController : ApiController
{
public HttpResponseMessage GET()
{
Console.WriteLine("Receive HTTP GET request");
Func<Stream, HttpContent, TransportContext, Task> func = (stream, httpContent, transportContext) =>
{
return Monitor(httpContent, stream);
};
HttpResponseMessage response = Request.CreateResponse();
response.StatusCode = HttpStatusCode.OK;
response.Content = new PushStreamContent(func, new MediaTypeHeaderValue("text/plain"));
return response;
}
async Task Monitor(HttpContent httpContent, Stream stream)
{
try
{
using (StreamWriter sw = new StreamWriter(stream, new UTF8Encoding(false)))
{
for (;;)
{
sw.WriteLine(Guid.NewGuid().ToString());
sw.Flush();
await Task.Delay(1000);
}
}
}
catch (CommunicationException ce)
{
HttpListenerException ex = ce.InnerException as HttpListenerException;
if (ex != null)
{
Console.WriteLine("HttpListenerException occurs, error code = {0}", ex.ErrorCode);
}
else
{
Console.WriteLine("{0} occurs : {1}", ce.GetType().Name, ce.Message);
}
}
catch (Exception ex)
{
Console.WriteLine("{0} occurs : {1}", ex.GetType().Name, ex.Message);
}
finally
{
stream.Close();
stream.Dispose();
httpContent.Dispose();
Console.WriteLine("Dispose");
}
}
}
打开 url http://127.0.0.1:54781/
,我看到渐进式响应即将到来。
但是如果客户端在服务端发送响应时终止连接,完成端口线程会被占用,永远不会释放。
它能够通过耗尽完成端口线程池来关闭应用程序。
改成OWIN自托管后,这个问题就消失了。这似乎是 System.Web.Http.SelfHost
这是更新后的代码:
class Program
{
static void Main(string[] args)
{
var server = WebApp.Start<Startup>(url: "http://*:54781");
for (;;)
{
int workerThreads, completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("Worker Threads = {0}; Available Completion Port Threads = {1};", workerThreads, completionPortThreads);
Console.Out.Flush();
Thread.Sleep(2000);
}
}
}
public class Startup
{
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}",
defaults: new { @controller = "Default" }
);
appBuilder.UseWebApi(config);
}
}
对于其他人来说 post System.Web.Http.SelfHost
中也有类似的问题,默认情况下使用 CPU 核心 * 100 个线程,这与默认的 ThreadPool 限制相同,结尾为Mono pre-creates 所有线程的死锁。
解决此问题的最简单方法是将 MONO_THREADS_PER_CPU
环境变量设置为大于 1 的值,或者在 运行 Mono 应用程序时使用 --server
标志。