当处理程序很慢时,为什么 Kestrel 不能同时处理更多的 HTTP 连接?
Why doesn't Kestrel handle more HTTP connections simultaneously when handlers are slow?
我有一个默认的 ASP .NET Core Web API 应用程序,只有一个处理程序:
[HttpGet("{x}")]
public string Get(string x)
{
var guid = Guid.NewGuid();
var start = DateTime.Now;
Console.WriteLine($"{guid}\t1\tSTRT\t{start}");
var sb = new StringBuilder();
using (var conn = new OracleConnection(CONN_STR)) {
using (var cmd = conn.CreateCommand()) {
conn.Open();
Console.WriteLine($"{guid}\t2\tCONN\t{DateTime.Now - start}");
cmd.CommandText = "select hello4(:x) from dual";
var nameParam = cmd.CreateParameter();
nameParam.ParameterName = "x";
nameParam.Value = x;
cmd.Parameters.Add(nameParam);
var ret = cmd.ExecuteScalar();
if (ret is string xname) {
sb.Append("{\"x\":");
sb.Append(x);
sb.Append("\",\"xname\":\"");
sb.Append(xname);
sb.Append("\"}");
} else {
sb.Append("{\"error\":\"no data found\"}");
}
}
}
Console.WriteLine($"{guid}\t3\tDONE\t{DateTime.Now - start}");
return sb.ToString();
}
我使用 vegeta 对其进行负载测试:vegeta attack -targets=targets.txt -duration=10s -rate=100 -timeout=0 | vegeta report
.
当 hello4
很快时,我可以在标准输出中看到处理程序每秒被调用 100 次。
当 hello4
包含 dbms_lock.sleep(1);
以模拟额外的处理时间时,我发现处理程序每秒被调用的次数要少得多,大约 20 次。实际上我预计它仍会被调用大约 100 次每秒,对数据库施加额外压力并耗尽 SGA(我的连接池限制是 1024)。
为什么没有发生这种情况,我如何强制它开始同时处理更多传入连接?
运行 cmd.ExecuteScalar
in a Task
是正确的想法,但它必须是一个很长的 运行 任务以避免阻塞应用程序池中的所有线程:
private static TaskFactory<object> tf = new TaskFactory<object>();
//and in the method
await tf.StartNew((Func<object>)cmd.ExecuteScalar, TaskCreationOptions.LongRunning).ConfigureAwait(false);
这允许 Kestrel 继续以它们到达的速率处理传入连接。
我有一个默认的 ASP .NET Core Web API 应用程序,只有一个处理程序:
[HttpGet("{x}")]
public string Get(string x)
{
var guid = Guid.NewGuid();
var start = DateTime.Now;
Console.WriteLine($"{guid}\t1\tSTRT\t{start}");
var sb = new StringBuilder();
using (var conn = new OracleConnection(CONN_STR)) {
using (var cmd = conn.CreateCommand()) {
conn.Open();
Console.WriteLine($"{guid}\t2\tCONN\t{DateTime.Now - start}");
cmd.CommandText = "select hello4(:x) from dual";
var nameParam = cmd.CreateParameter();
nameParam.ParameterName = "x";
nameParam.Value = x;
cmd.Parameters.Add(nameParam);
var ret = cmd.ExecuteScalar();
if (ret is string xname) {
sb.Append("{\"x\":");
sb.Append(x);
sb.Append("\",\"xname\":\"");
sb.Append(xname);
sb.Append("\"}");
} else {
sb.Append("{\"error\":\"no data found\"}");
}
}
}
Console.WriteLine($"{guid}\t3\tDONE\t{DateTime.Now - start}");
return sb.ToString();
}
我使用 vegeta 对其进行负载测试:vegeta attack -targets=targets.txt -duration=10s -rate=100 -timeout=0 | vegeta report
.
当 hello4
很快时,我可以在标准输出中看到处理程序每秒被调用 100 次。
当 hello4
包含 dbms_lock.sleep(1);
以模拟额外的处理时间时,我发现处理程序每秒被调用的次数要少得多,大约 20 次。实际上我预计它仍会被调用大约 100 次每秒,对数据库施加额外压力并耗尽 SGA(我的连接池限制是 1024)。
为什么没有发生这种情况,我如何强制它开始同时处理更多传入连接?
运行 cmd.ExecuteScalar
in a Task
是正确的想法,但它必须是一个很长的 运行 任务以避免阻塞应用程序池中的所有线程:
private static TaskFactory<object> tf = new TaskFactory<object>();
//and in the method
await tf.StartNew((Func<object>)cmd.ExecuteScalar, TaskCreationOptions.LongRunning).ConfigureAwait(false);
这允许 Kestrel 继续以它们到达的速率处理传入连接。