我们遇到Linux上运行mono 4.2.2 4.4的服务器从客户端接收多个传入TCP连接的情况。它还维护与另一个系统的单个传出TCP连接,并定期向另一个系统发出HTTP REST请求。在任意长度的时间之后,服务器停止运行 - 连接数量增加然后下降(见下图),线程转储显示大量连接,主要是来自客户端的传入连接,试图关闭(见下面的堆栈跟踪)。在服务器上运行netstat
会显示CLOSE_WAIT中存在大量连接。
据我们所知,连接的突然增加并不对应于实际的负载突然激增 - 新连接实际上是在两台不同机器上的服务器的两个实例之间进行平衡,另一台机器正在快乐地进行。在第一次失败后的几分钟内,另一台服务器确实看到了连接的相当大的增加,但是在一台服务器没有响应之后,这是预期的,并且在几分钟内而不是突然的峰值上升更加渐进
"Threadpool worker" at <unknown> <0xffffffff>
at (wrapper managed-to-native) System.Threading.WaitHandle.WaitOne_internal (System.Threading.WaitHandle,intptr,int,bool) <0xffffffff>
at System.Threading.WaitHandle.WaitOne () <0x0005c>
at System.Net.Sockets.Socket.EndSend (System.IAsyncResult,System.Net.Sockets.SocketError&) <0x0006f>
at System.Net.Sockets.Socket.EndSend (System.IAsyncResult) <0x0002b>
at System.Net.Sockets.NetworkStream.EndWrite (System.IAsyncResult) <0x00057>
at Mono.Security.Protocol.Tls.RecordProtocol.EndSendRecord (System.IAsyncResult) <0x000dc>
at Mono.Security.Protocol.Tls.RecordProtocol.SendRecord (Mono.Security.Protocol.Tls.ContentType,byte[]) <0x00037>
at Mono.Security.Protocol.Tls.RecordProtocol.SendAlert (Mono.Security.Protocol.Tls.Alert) <0x0009f>
at Mono.Security.Protocol.Tls.RecordProtocol.SendAlert (Mono.Security.Protocol.Tls.AlertDescription) <0x0004b>
at Mono.Security.Protocol.Tls.SslStreamBase.Dispose (bool) <0x0008b>
at Mono.Security.Protocol.Tls.SslServerStream.Dispose (bool) <0x00017>
at System.IO.Stream.Close () <0x00019>
at Mono.Security.Protocol.Tls.SslStreamBase.Close () <0x00014>
at System.IO.Stream.Dispose () <0x00013>
at (wrapper remoting-invoke-with-check) System.IO.Stream.Dispose () <0xffffffff>
at System.Net.Security.SslStream.Dispose (bool) <0x00037>
at System.IO.Stream.Close () <0x00019>
at CompanyName.Networking.Connection.Close () <0x00057>
at CompanyName.Networking.Connection.ProcessBytes (int) <0x00202>
at CompanyName.Networking.Connection.DataReceivedAsync (System.IAsyncResult) <0x0006f>
at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___object (object,intptr,intptr,intptr) <0xffffffff>
at <unknown> <0xffffffff>
at (wrapper managed-to-native) System.Runtime.Remoting.Messaging.AsyncResult.Invoke (System.Runtime.Remoting.Messaging.AsyncResult) <0xffffffff>
at System.Runtime.Remoting.Messaging.AsyncResult.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () <0x0000c>
at System.Threading.ThreadPoolWorkQueue.Dispatch () <0x001d6>
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () <0x00008>
at (wrapper runtime-invoke) <Module>.runtime_invoke_bool (object,intptr,intptr,intptr) <0xffffffff>
我们的代码中只有三行stacktrace。鉴于它们是DataReceivedAsync -> ProcessBytes -> Close
,这意味着较早的stream.BeginRead
触发了我们给它的回调(DataReceivedAsync
),它调用EndRead
并发现已经读取了0个字节。这会导致ProcessBytes
断定TCP连接已被另一端关闭,并调用我们的Close()
方法,该方法在Close()
和{{SslStream
上调用TcpClient
1}}。查看堆栈跟踪,Close()
上的SslStream
电话是我们遇到的问题。
通常的回答是“为什么我的连接卡在CLOSE_WAIT?”是“你需要关闭连接”。但是,我们试图关闭连接并发现框架代码挂起WaitHandle
。我们如何解决这个问题?