【发布时间】:2013-04-05 17:51:05
【问题描述】:
当服务器在 CPU 负载过重或线程池繁忙时出现 WCF 可靠会话故障
WCF 可靠会话中似乎存在设计缺陷,当服务器处于高 CPU 负载(80-100% 范围)或没有即时 IO 时,该缺陷会阻止基础结构保持活动消息的发布或接受可用于处理消息的线程池线程。由于可靠的会话不活动超时,症状表现为明显的随机通道中止。然而,中止逻辑似乎以更高的优先级或通过不同的机制运行,因为即使保活定时器无法运行,中止定时器似乎也会触发。
深入研究参考源,看来 ChannelReliableSession 使用 InterruptableTimer 类来处理 inactivityTimer。作为响应,它触发由 ReliableOutputSessionChannel 设置的 PollingCallback,它创建一个 ACKRequestedMessage 并将其发送到远程端点。 InactivityTimer 使用 WCF 内部的 IOThreadTimer/IOThreadScheduler 来调度自己。这取决于一个可用的(非繁忙的)IO ThreadPool 线程来为定时器提供服务。 如果 CPU 负载很高,则线程池似乎不会产生新线程。因此,如果有多个线程正在执行(在我的 4 核机器上似乎是 8 个线程;在 15 秒 inactivityTimeout 7 将中止并失败),则没有线程可用于发送 keep-alive。 但是,如果您将客户端上的可靠会话不活动超时修改为比服务器更长,即使在这些情况下,服务器仍将单方面中止通道,因为它希望在更短的时间内收到消息。因此,中止逻辑似乎以更高的优先级运行,或者向其中一个正在执行的线程抛出异常(不确定是哪个);我预计服务器上的中止会由于 CPU 高而延迟,并且客户端的超时时间较长最终会命中,但事实并非如此。 如果 CPU 负载较低,那么即使并发调用需要 30-90 秒才能返回,这种完全相同的场景也可以正常工作。
您的 InstanceMode 是什么,最大并发连接数、会话数或实例数,以及其他任何超时值是什么都无关紧要(recieveTimeout 必须大于 inactivityTimeout 除外)。这完全是WCF实现的设计缺陷;它应该使用隔离的高优先级或实时线程来为保持活动消息提供服务,因此不会产生虚假的中止。
简短的版本是:只要 CPU 负载保持在低水平,我可以发出 1000 个需要 60 秒才能完成的并发请求,并有 15 秒的可靠会话不活动超时没有问题。只要 CPU 负载变得繁重时,调用将随机开始中止,包括不占用任何 CPU 时间的调用或空闲等待使用的双工会话。如果传入调用也增加了 CPU 负载,那么服务将进入死亡螺旋,因为执行时间浪费在保证中止的请求上,而其他请求则位于入站队列中。在停止所有请求、所有运行中的线程完成并且 CPU 负载下降之前,该服务无法恢复到健康状态。 这种行为似乎使 Reliable Sessions 成为最不可靠的通信机制之一。
同样的行为也适用于客户端;在这种情况下,WCF 客户端可能会受到盒子上其他进程的支配,但在高 CPU 负载下,它将随机中止可靠会话,除非所有操作的完成时间少于 inactivityTimeout,但如果您不发出新的调用很快WCF可能仍然无法发送keep-alive并且通道可能出现故障。
【问题讨论】:
标签: c# wcf reliablesession