托管在 Azure App 服务上的 WCF 服务似乎永远不会完成为处理而打开的线程
WCF service hosted on Azure App service never seems to finish threads opened for processing
我已将 WCF 服务部署到 Azure 应用服务,该服务仅执行一项任务 - 向主题发送消息。尽管应用程序在正常负载下运行良好,但一旦应用程序负载增加,它就会开始遇到更高的线程数。
当达到线程数限制时,应用程序实例变得不健康。
那些线程永远处于等待状态。我们在线程计数指标上尝试了横向扩展选项,但该应用程序只是继续添加更多实例,因为较早的实例仍然有几乎所有线程在等待并且永远保持不健康状态。
这是按以下顺序执行的。
- 接受请求。
- 初始化服务总线主题客户端
- 将请求的消息发送到主题。
- 关闭主题客户端。
在突发发送1000个请求时,应用可以正常运行,但启动的线程数始终处于等待状态。但是,在这些线程等待期间,CPU 保持在 0%。此服务的平均响应时间也低于 100 毫秒。
向该服务发送 1000 个请求后,我看到打开的线程数量相似。
此问题的潜在根本原因是什么?我发送消息到主题的代码有问题吗?
public async Task SendAsync(Message message)
{
try
{
await _topicClient.SendAsync(message);
}
catch(Exception exc)
{
throw new Exception(exc.Message);
}
finally
{
await _topicClient.CloseAsync();
}
}
enter image description here
您提供的代码示例并没有告诉我们太多信息。我们不知道 SendAsync(Message message)
是如何被调用的。在接受更多消息之前,您的 image 队列计数是否下降到 0?我假设客户端调用您的 WCF 应用程序服务,告诉它发送消息到服务总线?
听起来确实像是在敲 1000 maximum connections。您的 _topicClinet
应该是所有客户端都使用的应用程序域的单例。如果您所做的只是消息转发,您也应该只需要一个应用程序服务实例。除非有更多您没有提到的处理,否则不需要缩放。
查看 Service Bus messaging best practices doc 以获得更多建议。
感谢您的回复。这些都是很好的建议,我会根据这些建议检查我的实施。
好消息是我能够解决这个问题,它与我之前认为的主题客户端无关。这是由于我注册依赖注入的方式。
我正在实现一个基于 .Net Framework 4.8 的 WCF 服务,最初,我们没有包含 Global.asax 但在服务控制器构造函数中注册了 DI。该实现一直有效,直到我们意识到(作为性能测试的一部分)当我们添加 ILogger 依赖项时它似乎添加了额外的线程。这些额外的线程永远不会冷却,但会随着服务收到更多请求而增加。
为了解决,我将 DI 注册移到了 global.asax 中的 Application_Start。
我已将 WCF 服务部署到 Azure 应用服务,该服务仅执行一项任务 - 向主题发送消息。尽管应用程序在正常负载下运行良好,但一旦应用程序负载增加,它就会开始遇到更高的线程数。 当达到线程数限制时,应用程序实例变得不健康。
那些线程永远处于等待状态。我们在线程计数指标上尝试了横向扩展选项,但该应用程序只是继续添加更多实例,因为较早的实例仍然有几乎所有线程在等待并且永远保持不健康状态。
这是按以下顺序执行的。
- 接受请求。
- 初始化服务总线主题客户端
- 将请求的消息发送到主题。
- 关闭主题客户端。
在突发发送1000个请求时,应用可以正常运行,但启动的线程数始终处于等待状态。但是,在这些线程等待期间,CPU 保持在 0%。此服务的平均响应时间也低于 100 毫秒。
向该服务发送 1000 个请求后,我看到打开的线程数量相似。
此问题的潜在根本原因是什么?我发送消息到主题的代码有问题吗?
public async Task SendAsync(Message message)
{
try
{
await _topicClient.SendAsync(message);
}
catch(Exception exc)
{
throw new Exception(exc.Message);
}
finally
{
await _topicClient.CloseAsync();
}
}
enter image description here
您提供的代码示例并没有告诉我们太多信息。我们不知道 SendAsync(Message message)
是如何被调用的。在接受更多消息之前,您的 image 队列计数是否下降到 0?我假设客户端调用您的 WCF 应用程序服务,告诉它发送消息到服务总线?
听起来确实像是在敲 1000 maximum connections。您的 _topicClinet
应该是所有客户端都使用的应用程序域的单例。如果您所做的只是消息转发,您也应该只需要一个应用程序服务实例。除非有更多您没有提到的处理,否则不需要缩放。
查看 Service Bus messaging best practices doc 以获得更多建议。
感谢您的回复。这些都是很好的建议,我会根据这些建议检查我的实施。
好消息是我能够解决这个问题,它与我之前认为的主题客户端无关。这是由于我注册依赖注入的方式。
我正在实现一个基于 .Net Framework 4.8 的 WCF 服务,最初,我们没有包含 Global.asax 但在服务控制器构造函数中注册了 DI。该实现一直有效,直到我们意识到(作为性能测试的一部分)当我们添加 ILogger 依赖项时它似乎添加了额外的线程。这些额外的线程永远不会冷却,但会随着服务收到更多请求而增加。
为了解决,我将 DI 注册移到了 global.asax 中的 Application_Start。