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