突发请求时客户端上的 wcf 死锁
Posted
技术标签:
【中文标题】突发请求时客户端上的 wcf 死锁【英文标题】:wcf deadlock on client when bursting requests 【发布时间】:2012-07-23 14:27:28 【问题描述】:至少一周以来,我一直在努力解决这个问题(也学到了一些新东西 - WCF 是主要的 PITA)。
这是我的问题:我的应用程序中有一个场景,有时会冻结整个客户端,例如永远冻结(因为我禁用了超时,因为客户端和服务器都处于受控环境中)。死锁完全发生在同一个调用上,我推测是由于它之前的请求突发。
检查客户端上的死锁堆栈跟踪给了我这个:
[In a sleep, wait, or join]
WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + 0x26 bytes
mscorlib.dll!System.Threading.SynchronizationContext.InvokeWaitMethodHelper(System.Threading.SynchronizationContext syncContext, System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + 0x1c bytes
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) + 0x2b bytes
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext) + 0x2d bytes
mscorlib.dll!System.Threading.WaitHandle.WaitOne() + 0x10 bytes
System.Runtime.DurableInstancing.dll!System.Runtime.TimeoutHelper.WaitOne(System.Threading.WaitHandle waitHandle, System.TimeSpan timeout) + 0x7c bytes
System.ServiceModel.dll!System.ServiceModel.Channels.OverlappedContext.WaitForSyncOperation(System.TimeSpan timeout, ref object holder) + 0x40 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.PipeConnection.WaitForSyncRead(System.TimeSpan timeout, bool traceExceptionsAsErrors) + 0x38 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.PipeConnection.Read(byte[] buffer, int offset, int size, System.TimeSpan timeout) + 0xef bytes
System.ServiceModel.dll!System.ServiceModel.Channels.DelegatingConnection.Read(byte[] buffer, int offset, int size, System.TimeSpan timeout) + 0x21 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(System.ServiceModel.Channels.StreamUpgradeInitiator upgradeInitiator, ref System.ServiceModel.Channels.IConnection connection, System.ServiceModel.Channels.ClientFramingDecoder decoder, System.ServiceModel.IDefaultCommunicationTimeouts defaultTimeouts, ref System.Runtime.TimeoutHelper timeoutHelper) + 0xb3 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(System.ServiceModel.Channels.IConnection connection, System.ArraySegment<byte> preamble, ref System.Runtime.TimeoutHelper timeoutHelper) + 0x155 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(System.ServiceModel.Channels.IConnection connection, ref System.Runtime.TimeoutHelper timeoutHelper) + 0x25 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(System.TimeSpan timeout) + 0xe2 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(System.TimeSpan timeout) + 0x37 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.CommunicationObject.Open(System.TimeSpan timeout) + 0x13f bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.OnOpen(System.TimeSpan timeout) + 0x52 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.CommunicationObject.Open(System.TimeSpan timeout) + 0x13f bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(System.ServiceModel.Channels.ServiceChannel channel, System.TimeSpan timeout) + 0x12 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(System.TimeSpan timeout, System.ServiceModel.Channels.ServiceChannel.CallOnceManager cascade) + 0x10c bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.Call(string action, bool oneway, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, object[] ins, object[] outs, System.TimeSpan timeout) + 0x18b bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage methodCall, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation) + 0x59 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage message) + 0x65 bytes
mscorlib.dll!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref System.Runtime.Remoting.Proxies.MessageData msgData, int type) + 0xee bytes
MyService.dll!MyService.Controller.CallMethod() + 0x9 bytes
我怀疑突发调用序列的原因是,如果我在调用之前插入 60 秒的休眠,就不会发生死锁。
有人对如何避免这个问题有任何建议吗?
附:我正在使用命名管道。
编辑:
客户端对 WCF 服务的调用发生在 GUI 线程上。我是否正确地假设(从调用堆栈)它试图访问导致死锁的 GUI 线程?
编辑:
客户端通道工厂初始化:
var binding = new NetNamedPipeBinding
OpenTimeout = TimeSpan.MaxValue,
CloseTimeout = TimeSpan.MaxValue,
SendTimeout = TimeSpan.MaxValue,
ReceiveTimeout = TimeSpan.MaxValue,
ReaderQuotas = MaxStringContentLength = Int32.MaxValue, MaxArrayLength = Int32.MaxValue ,
MaxBufferPoolSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue,
MaxReceivedMessageSize = Int32.MaxValue
;
CustomBinding pipeBinding = new CustomBinding(binding);
pipeBinding.Elements.Find<NamedPipeTransportBindingElement>().ConnectionPoolSettings.IdleTimeout = TimeSpan.FromDays(24);
channelFactory = new ChannelFactory<ITestsModule>(pipeBinding,
new EndpointAddress(string.Format("net.pipe://localhost/app_0/TestsModule", ProcessId)));
服务器端主机初始化:
var host = new ServiceHost(m_testModule, new Uri[] new Uri(string.Format("net.pipe://localhost/app_0", Process.GetCurrentProcess().Id)) );
ServiceThrottlingBehavior throttle = host.Description.Behaviors.Find<ServiceThrottlingBehavior>();
if (throttle == null)
throttle = new ServiceThrottlingBehavior();
throttle.MaxConcurrentCalls = 500;
throttle.MaxConcurrentSessions = 200;
throttle.MaxConcurrentInstances = 100;
host.Description.Behaviors.Add(throttle);
ThreadPool.SetMinThreads(1000, 1000);
var binding = new NetNamedPipeBinding
OpenTimeout = TimeSpan.MaxValue,
CloseTimeout = TimeSpan.MaxValue,
SendTimeout = TimeSpan.MaxValue,
ReceiveTimeout = TimeSpan.MaxValue,
ReaderQuotas = MaxStringContentLength = Int32.MaxValue, MaxArrayLength = Int32.MaxValue ,
MaxBufferPoolSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue,
MaxReceivedMessageSize = Int32.MaxValue
;
CustomBinding pipeBinding = new CustomBinding(binding);
pipeBinding.Elements.Find<NamedPipeTransportBindingElement>().ConnectionPoolSettings.IdleTimeout = TimeSpan.FromDays(24);
host.AddServiceEndpoint(typeof(ITestsModule), pipeBinding, "TestsModule");
服务类行为:
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple,
UseSynchronizationContext = false,
IncludeExceptionDetailInFaults = true
)]
【问题讨论】:
请提供更多关于 WCF 配置的详细信息(绑定、实例模式、并发等) 更新了 WCF 配置的详细信息... 您如何托管服务? IIS?视窗服务?界面应用? 我认为您应该考虑更实用的超时值。 29247年有点多…… “请求突发”是什么意思?您是否在此特定服务之前多次调用该服务? 【参考方案1】:首先,你知道你在服务器端锁定了什么吗?锁争用是否仅来自 WCF 接口?或者您的服务器也从其他地方的其他组件/类锁定?这是最重要的问题,与 WCF 无关。
现在,这就是说,试试这个来帮助缩小问题范围:
选项 1: 客户端超时 - 不要设置为 Int32.MaxValue,设置为十秒,并在超时时执行客户端重试。
选项 2:
ServiceThrottlingBehavior ThrottleBehavior = new ServiceThrottlingBehavior();
ThrottleBehavior.MaxConcurrentSessions = 4;
ThrottleBehavior.MaxConcurrentCalls = 4;
ThrottleBehavior.MaxConcurrentInstances = 4;
ServiceHost Host = ...
Host.Description.Behaviors.Add(ThrottleBehavior);
如果 OPTION 2 有帮助,请对其进行压力测试(应该对 OPTION 1 做同样的事情)- 如果 MaxConcurrentXXX 设置为一个大数字,还要注意线程计数的增加。
希望对你有帮助
【讨论】:
以上是关于突发请求时客户端上的 wcf 死锁的主要内容,如果未能解决你的问题,请参考以下文章
通过 HTTPS 上的客户端证书对 WCF 请求进行身份验证