我创建了一个简单的WCF双工服务作为概念验证/学习练习,定义如下:
// web service definition
[ServiceContract(CallbackContract = typeof(IDuplexClientCallback))]
public interface IDuplexWebService
{
[OperationContract(IsOneWay = true)]
void Command(CommandRequest command);
}
// service implementation decorated with:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
// callback contract
public interface IDuplexClientCallback
{
[OperationContract(IsOneWay = true)]
void Send(string message);
}
// callback implementation decorated with:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Single)]
要求双工的原因是服务需要向连接的客户端发送未经请求的更新。正常的使用周期是:
这很常见,但偶尔从Service到Client的Send()回调阻塞60秒(超时),导致Service无法接受新命令。似乎没有太多的模式,当发生这种情况时,在被阻止的命令之间可以有少至两个和多达二十个成功的命令。
每个客户端创建并缓存一个Channel以与服务进行通信。同样,服务为每个客户端创建并缓存一个CallbackChannel。我正在使用wsHttpBinding和很大程度上的默认设置(相对较新的WCF)。通过调试,我确定通道没有过早关闭。客户端回调的实现只是将收到的消息写入控制台。
我在服务器和客户端都添加了一些日志记录语句,希望能够证明这个问题:
成功的命令,你可以看到很快执行:
17:12:35->INFO: [CLIENT] Submitting command: hello world
17:12:35->INFO: [SERVER] Received command request.
17:12:35->INFO: [SERVER] Queuing action.
17:12:35->INFO: [SERVER] Action queued, exiting.
17:12:35->INFO: [SERVER] Processing complete. Sending response...
17:12:35->INFO: [SERVER] Finished sending response. Took: 0.0068685 seconds.
17:12:35->INFO: [CLIENT] Received response of length 21.
被阻止的命令:
17:12:49->INFO: [CLIENT] Submitting command: hello moon
17:12:49->INFO: [SERVER] Received command request.
17:12:49->INFO: [SERVER] Queuing action.
17:12:49->INFO: [SERVER] Action queued, exiting.
17:12:49->INFO: [SERVER] Processing complete. Sending response...
17:12:49->INFO: [CLIENT] Received response of length 21.
17:12:51->INFO: [CLIENT] Submitting command: hello mars
17:12:51->INFO: [SERVER] Received command request.
17:12:51->INFO: [SERVER] Queuing action.
17:12:51->INFO: [SERVER] Action queued, exiting.
17:13:49->INFO: [SERVER] Finished sending response. Took: 60.0077729 seconds. ('hello moon')
17:13:49->INFO: [SERVER] Processing complete. Sending response...
17:13:49->INFO: [SERVER] Finished sending response. Took: 0.0076397 seconds. ('hello mars')
17:13:49->INFO: [CLIENT] Received response of length 21.
此处向'hello moon'发送响应已花费60秒(在此期间另一个命令已排队)。这里60秒的数字必须是相关的 - 有些东西正在阻塞并超时。您还可以看到客户端已几乎立即收到响应,但服务器未注册发送操作完成60秒。
我还启用了跟踪,发现此错误被抛出。对我来说,单向操作会超时等待回复似乎很奇怪。
System.TimeoutException:HTTP请求 ' [本地主机-URL] / Temporary_Listen_Addresses / a03cfb7c-7e6d-4444-9914-11921f85c277 / 89a796b2-aad7-49ad-8426-c2a91ddf46c0 ' 已超过分配的超时00:00:59.9980000。时间 分配给这个操作可能是一个更长的一部分 超时。在System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException,HttpWebRequest request,HttpAbortReason abortReason) at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout) 在System.ServiceModel.Channels.RequestChannel.Request(消息消息,TimeSpan超时) 在System.ServiceModel.Channels.RequestOneWayChannelFactory.RequestOutputChannel.OnSend(消息消息,TimeSpan超时) 在System.ServiceModel.Channels.OutputChannel.Send(消息消息,TimeSpan超时)
我一直在努力诊断这一天并且已经撞墙了。我已经为服务尝试了不同的并发模式,问题仍然存在。这可能是什么原因,以及我可以采取哪些进一步措施来追踪它?我需要做些什么来管理我的联系吗?我需要调整一些配置吗?如果有必要,我可以添加更多代码,但这是一个很长的问题。