将 HttpClient 与 NServiceBus 结合使用时出现严重延迟
Serious delay using HttpClient in combination with NServiceBus
我正在使用 NServiceBus 进行集成 POC。当发生某些事情时,此集成会向 API 发送通知。
一切正常,但在执行并发 http 请求时会出现严重的性能问题。
我配置了一个端点来做一件事:发送 json 请求(通知)到 REST API。
我的设置:
端点(框架 4.6.1)有 1 个静态 HttpClient,仅在程序启动时初始化一次:
internal static void Intitialize()
{
ServicePointManager.DefaultConnectionLimit = 100;
var apiSettings = NotificationEngineManager.GetInterfaceSettings()?.API;
NotificationClient = new HttpClient();
NotificationClient.Timeout = TimeSpan.FromSeconds(45);
if (!string.IsNullOrEmpty(apiSettings.UserName))
{
var byteArray = Encoding.ASCII.GetBytes($"{apiSettings.UserName}:{apiSettings.Password}");
NotificationClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
}
}
internal static HttpClient NotificationClient { get; set; }
我的处理程序负责 post 请求:
public async Task Handle(EventRegistered message, IMessageHandlerContext context)
{
var apiSettings = NotificationEngineManager.GetInterfaceSettings()?.API;
if (apiSettings == null)
{
throw new BusinessException("No valid API settings found");
}
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DateFormatString = "s";
settings.Converters.Add(new StringEnumConverter { });
try
{
var jsonRequest = JsonConvert.SerializeObject(message.Notification, settings);
var response = await HttpClients.NotificationClient.PostAsync(apiSettings.Endpoint, new StringContent(jsonRequest, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
catch (TaskCanceledException)
{
throw new Exception("Notifiaction Request timed out.");
}
await context.Publish(new NotificationSent(message, DateTime.Now)).ConfigureAwait(false);
}
问题:
NServiceBus 端点同时处理 10 条消息,即同时处理 10 个 http post 请求。
Rest API 大约需要 0.2s 来响应 1 条消息。但是同时触发 10 个 http post 请求大约需要 35 秒。
请求全部同时开始,也全部同时结束,也就是大约35秒后。
使用 netstat
命令我可以看到 10 个打开的 tcp 连接到 API。所以 HttpClient 确实处理并发请求。
奇怪的是,如果我将我的端点并发设置为 1,意味着 1 对 1 处理 http 请求,所有 10 个请求都在 2 秒内处理。
如果我将端点并发级别设置为 20 或更高,几乎所有请求都会超时(超时 = 45 秒)。
可以使用 HttpWebRequest 而不是 HttpClient 重现相同内容。
I'm at a loss here. What do I miss? It looks like all the requests are
waiting on each other, either on the client or the server. Any help is
very much appreciated.
Rest API 是 ISS 上的基本 .NET Core API 运行。
// POST api/notification
[HttpPost]
[Authorize]
public async Task<IActionResult> Post([FromBody] Notification notification)
{
try
{
return Accepted();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
更新:
客户端 (nservicebus) 和服务器端 (api) 都记录。
运行 在本地主机上处于调试模式并且很容易重现。
客户端
log.Info($"Start request {message.Event.Id.ToString()}");
var response = await HttpClients.NotificationClient.PostAsync(apiSettings.Endpoint, new StringContent(jsonRequest, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
log.Info($"Stop request {message.Event.Id.ToString()}");
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 8 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 10 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 5 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 2 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 4 Start request
2018-04-18 09:05:23.6336 INFO NOTIFY_WO_OUT 6 Start request
2018-04-18 09:05:24.6329 INFO NOTIFY_WO_OUT 9 Start request
2018-04-18 09:05:25.6142 INFO NOTIFY_WO_OUT 1 Start request
2018-04-18 09:05:26.6544 INFO NOTIFY_WO_OUT 7 Start request
2018-04-18 09:05:27.6545 INFO NOTIFY_WO_OUT 3 Start request
2018-04-18 09:05:47.2034 INFO NOTIFY_WO_OUT 5 Stop request
2018-04-18 09:05:47.6285 INFO NOTIFY_WO_OUT 3 Stop request
2018-04-18 09:05:47.9855 INFO NOTIFY_WO_OUT 6 Stop request
2018-04-18 09:05:48.0550 INFO NOTIFY_WO_OUT 9 Stop request
2018-04-18 09:05:48.5226 INFO NOTIFY_WO_OUT 4 Stop request
2018-04-18 09:05:48.5526 INFO NOTIFY_WO_OUT 10 Stop request
2018-04-18 09:05:48.9987 INFO NOTIFY_WO_OUT 8 Stop request
2018-04-18 09:05:49.0347 INFO NOTIFY_WO_OUT 1 Stop request
2018-04-18 09:05:49.0647 INFO NOTIFY_WO_OUT 7 Stop request
2018-04-18 09:05:49.3017 INFO NOTIFY_WO_OUT 2 Stop request
服务器端日志记录。
日志记录中间件添加到管道的开头。
2018-04-18 09:05:38.7660 INFO Begin Request 6
2018-04-18 09:05:38.7850 INFO message 6 received
2018-04-18 09:05:38.8150 INFO End Request 6
2018-04-18 09:05:39.4036 INFO Begin Request 9
2018-04-18 09:05:39.4231 INFO message 9 received
2018-04-18 09:05:39.4551 INFO End Request 9
2018-04-18 09:05:39.7172 INFO Begin Request 10
2018-04-18 09:05:39.7512 INFO message 10 received
2018-04-18 09:05:39.7512 INFO Begin Request 8
2018-04-18 09:05:39.7812 INFO End Request 10
2018-04-18 09:05:39.8132 INFO message 8 received
2018-04-18 09:05:39.8302 INFO End Request 8
2018-04-18 09:05:40.0722 INFO Begin Request 1
2018-04-18 09:05:40.1097 INFO message 1 received
2018-04-18 09:05:40.1097 INFO Begin Request 5
2018-04-18 09:05:40.1413 INFO message 5 received
2018-04-18 09:05:40.1703 INFO End Request 1
2018-04-18 09:05:40.1913 INFO End Request 5
2018-04-18 09:05:40.4033 INFO Begin Request 3
2018-04-18 09:05:40.4403 INFO message 3 received
2018-04-18 09:05:40.4563 INFO End Request 3
2018-04-18 09:05:40.5173 INFO Begin Request 7
2018-04-18 09:05:40.5508 INFO message 7 received
2018-04-18 09:05:40.5663 INFO End Request 7
2018-04-18 09:05:41.0100 INFO Begin Request 4
2018-04-18 09:05:41.0470 INFO message 4 received
2018-04-18 09:05:41.0630 INFO End Request 4
2018-04-18 09:05:41.3535 INFO Begin Request 2
2018-04-18 09:05:41.3740 INFO message 2 received
2018-04-18 09:05:41.4080 INFO End Request 2
正在查看请求 8。
请求是在 .23 的代码中发起的。服务器在 0.39 收到请求。所以有16秒的差距
我正在使用 System.Diagnostics 和 NLog 记录网络跟踪。
这导致了严重的性能下降。
禁用 System.Diagnostics 解决了我的问题。
我正在使用 NServiceBus 进行集成 POC。当发生某些事情时,此集成会向 API 发送通知。
一切正常,但在执行并发 http 请求时会出现严重的性能问题。
我配置了一个端点来做一件事:发送 json 请求(通知)到 REST API。
我的设置:
端点(框架 4.6.1)有 1 个静态 HttpClient,仅在程序启动时初始化一次:
internal static void Intitialize()
{
ServicePointManager.DefaultConnectionLimit = 100;
var apiSettings = NotificationEngineManager.GetInterfaceSettings()?.API;
NotificationClient = new HttpClient();
NotificationClient.Timeout = TimeSpan.FromSeconds(45);
if (!string.IsNullOrEmpty(apiSettings.UserName))
{
var byteArray = Encoding.ASCII.GetBytes($"{apiSettings.UserName}:{apiSettings.Password}");
NotificationClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
}
}
internal static HttpClient NotificationClient { get; set; }
我的处理程序负责 post 请求:
public async Task Handle(EventRegistered message, IMessageHandlerContext context)
{
var apiSettings = NotificationEngineManager.GetInterfaceSettings()?.API;
if (apiSettings == null)
{
throw new BusinessException("No valid API settings found");
}
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DateFormatString = "s";
settings.Converters.Add(new StringEnumConverter { });
try
{
var jsonRequest = JsonConvert.SerializeObject(message.Notification, settings);
var response = await HttpClients.NotificationClient.PostAsync(apiSettings.Endpoint, new StringContent(jsonRequest, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
catch (TaskCanceledException)
{
throw new Exception("Notifiaction Request timed out.");
}
await context.Publish(new NotificationSent(message, DateTime.Now)).ConfigureAwait(false);
}
问题:
NServiceBus 端点同时处理 10 条消息,即同时处理 10 个 http post 请求。
Rest API 大约需要 0.2s 来响应 1 条消息。但是同时触发 10 个 http post 请求大约需要 35 秒。 请求全部同时开始,也全部同时结束,也就是大约35秒后。
使用 netstat
命令我可以看到 10 个打开的 tcp 连接到 API。所以 HttpClient 确实处理并发请求。
奇怪的是,如果我将我的端点并发设置为 1,意味着 1 对 1 处理 http 请求,所有 10 个请求都在 2 秒内处理。
如果我将端点并发级别设置为 20 或更高,几乎所有请求都会超时(超时 = 45 秒)。
可以使用 HttpWebRequest 而不是 HttpClient 重现相同内容。
I'm at a loss here. What do I miss? It looks like all the requests are waiting on each other, either on the client or the server. Any help is very much appreciated.
Rest API 是 ISS 上的基本 .NET Core API 运行。
// POST api/notification
[HttpPost]
[Authorize]
public async Task<IActionResult> Post([FromBody] Notification notification)
{
try
{
return Accepted();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
更新: 客户端 (nservicebus) 和服务器端 (api) 都记录。 运行 在本地主机上处于调试模式并且很容易重现。
客户端
log.Info($"Start request {message.Event.Id.ToString()}");
var response = await HttpClients.NotificationClient.PostAsync(apiSettings.Endpoint, new StringContent(jsonRequest, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
log.Info($"Stop request {message.Event.Id.ToString()}");
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 8 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 10 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 5 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 2 Start request
2018-04-18 09:05:23.2886 INFO NOTIFY_WO_OUT 4 Start request
2018-04-18 09:05:23.6336 INFO NOTIFY_WO_OUT 6 Start request
2018-04-18 09:05:24.6329 INFO NOTIFY_WO_OUT 9 Start request
2018-04-18 09:05:25.6142 INFO NOTIFY_WO_OUT 1 Start request
2018-04-18 09:05:26.6544 INFO NOTIFY_WO_OUT 7 Start request
2018-04-18 09:05:27.6545 INFO NOTIFY_WO_OUT 3 Start request
2018-04-18 09:05:47.2034 INFO NOTIFY_WO_OUT 5 Stop request
2018-04-18 09:05:47.6285 INFO NOTIFY_WO_OUT 3 Stop request
2018-04-18 09:05:47.9855 INFO NOTIFY_WO_OUT 6 Stop request
2018-04-18 09:05:48.0550 INFO NOTIFY_WO_OUT 9 Stop request
2018-04-18 09:05:48.5226 INFO NOTIFY_WO_OUT 4 Stop request
2018-04-18 09:05:48.5526 INFO NOTIFY_WO_OUT 10 Stop request
2018-04-18 09:05:48.9987 INFO NOTIFY_WO_OUT 8 Stop request
2018-04-18 09:05:49.0347 INFO NOTIFY_WO_OUT 1 Stop request
2018-04-18 09:05:49.0647 INFO NOTIFY_WO_OUT 7 Stop request
2018-04-18 09:05:49.3017 INFO NOTIFY_WO_OUT 2 Stop request
服务器端日志记录。 日志记录中间件添加到管道的开头。
2018-04-18 09:05:38.7660 INFO Begin Request 6
2018-04-18 09:05:38.7850 INFO message 6 received
2018-04-18 09:05:38.8150 INFO End Request 6
2018-04-18 09:05:39.4036 INFO Begin Request 9
2018-04-18 09:05:39.4231 INFO message 9 received
2018-04-18 09:05:39.4551 INFO End Request 9
2018-04-18 09:05:39.7172 INFO Begin Request 10
2018-04-18 09:05:39.7512 INFO message 10 received
2018-04-18 09:05:39.7512 INFO Begin Request 8
2018-04-18 09:05:39.7812 INFO End Request 10
2018-04-18 09:05:39.8132 INFO message 8 received
2018-04-18 09:05:39.8302 INFO End Request 8
2018-04-18 09:05:40.0722 INFO Begin Request 1
2018-04-18 09:05:40.1097 INFO message 1 received
2018-04-18 09:05:40.1097 INFO Begin Request 5
2018-04-18 09:05:40.1413 INFO message 5 received
2018-04-18 09:05:40.1703 INFO End Request 1
2018-04-18 09:05:40.1913 INFO End Request 5
2018-04-18 09:05:40.4033 INFO Begin Request 3
2018-04-18 09:05:40.4403 INFO message 3 received
2018-04-18 09:05:40.4563 INFO End Request 3
2018-04-18 09:05:40.5173 INFO Begin Request 7
2018-04-18 09:05:40.5508 INFO message 7 received
2018-04-18 09:05:40.5663 INFO End Request 7
2018-04-18 09:05:41.0100 INFO Begin Request 4
2018-04-18 09:05:41.0470 INFO message 4 received
2018-04-18 09:05:41.0630 INFO End Request 4
2018-04-18 09:05:41.3535 INFO Begin Request 2
2018-04-18 09:05:41.3740 INFO message 2 received
2018-04-18 09:05:41.4080 INFO End Request 2
正在查看请求 8。 请求是在 .23 的代码中发起的。服务器在 0.39 收到请求。所以有16秒的差距
我正在使用 System.Diagnostics 和 NLog 记录网络跟踪。 这导致了严重的性能下降。
禁用 System.Diagnostics 解决了我的问题。