我有一台服务器和客户端进程都在同一台机器上运行。客户端创建一个CAO对象并使用它一段时间(<1s到几小时)。它占用了大量内存,所以我希望在客户端完成后尽快处理掉这个对象。
我将InitialLeaseTime和RenewOnCallTime设置为10s(0.1s和15s具有相同的问题)。我可以看到赞助商的Renweal功能每10分钟就会被召唤几分钟。几分钟后,客户开始做不同类型的工作,赞助商停止被叫(这似乎是错误的)。几分钟后,当客户端尝试使用远程对象时,它会抛出一个异常,说它已断开连接(可能是因为赞助商长时间没有被调用)。
似乎租赁经理在一段时间后停止尝试检查租约。
答案 0 :(得分:1)
我通过将赞助商放在服务器上而不是客户端来解决这个问题。服务器端赞助商似乎可以被可靠地调用,以使远程对象保持活跃状态。
Class SelfSponsor
Implements ISponsor
Public Function Renewal(ByVal lease As ILease) As System.TimeSpan Implements ISponsor.Renewal
Return lease.RenewOnCallTime
End Function
End Class
在MarshalByRef远程对象的类中:
Private Sponsor As SelfSponsor
Public Sub SponsorYourself()
Sponsor = New SelfSponsor
DirectCast(GetLifetimeService(), ILease).Register(Sponsor)
End Sub
Public Sub UnSponsorYourself()
DirectCast(GetLifetimeService(), ILease).Unregister(Sponsor)
End Sub
如果将SponsorYourself()代码放置在构造函数中,那么客户端会在创建对象后立即调用SponsorYourself。
如果服务器始终在运行并且客户端来去,这将是一个糟糕的解决方案,因为如果客户端异常退出而没有显式调用UnsponsorYourself(),那么该对象将永远保持活跃状态。但在我的情况下,服务器由客户端启动和停止,因此无关紧要。
答案 1 :(得分:1)
长时间的中间响应,但我认为其他人也可能遇到这个问题,所以这里。
我建议在VS中附加到您的服务器,进入“调试”菜单,选择“例外”,&#39;然后检查System.Net.Sockets.SocketException&#39;例外。这将在发生的任何套接字异常时中断程序。
在我的情况下,我最近开始看到这个问题,并且在经过大量调试后注意到在Lease Manager停止检查租约之前,发生了SocketException。在我的例子中,套接字异常是AddressChangedCallback
,堆栈跟踪:
1 [External Code]
2 System.dll!System.Net.Dns.TryGetAddrInfo(string name = "me.win.mycompany.com", System.Net.AddressInfoHints flags, out System.Net.IPHostEntry hostinfo = null)
3 System.dll!System.Net.Dns.GetAddrInfo(string name)
4 System.dll!System.Net.Dns.InternalGetHostByName(string hostName, bool includeIPv6)
5 System.dll!System.Net.Dns.GetHostEntry(string hostNameOrAddress)
6 System.Runtime.Remoting.dll!System.Runtime.Remoting.Channels.CoreChannel.UpdateCachedIPAddresses()
7 System.Runtime.Remoting.dll!System.Runtime.Remoting.Channels.CoreChannel.OnNetworkAddressChanged(object sender = null, System.EventArgs e = {System.EventArgs})
8 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
9 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
10 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
11 System.dll!System.Net.NetworkInformation.NetworkChange.AddressChangeListener.AddressChangedCallback(object stateObject, bool signaled)
12 mscorlib.dll!System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(object state, bool timedOut)
13 [External Code]
这个AddressChangedCallback,
在我看来似乎与网络适配器关闭或被更改有关(您可以通过按住windows+r
然后键入ncpa.cpl
来查看网络适配器 - 如果您有两个或更多可能,这个事件是由您在它们之间切换引起的)似乎导致套接字停止读取。这意味着下次LeaseManager使用远程连接来检查远程租约时,它无法从死套接字读取该租约。因此,它做了合理的事情 - 断开赞助商,因为我们不能再阅读它,并将其从对象的赞助商列表中删除。由于它可能是相关对象的唯一赞助商,因此该对象获得LeaseManger的非赞助,让GC最终可以自由选择。
解决此问题的一种方法是,在InitializeLifetimeService()
方法中,返回null而不是设置超时。这绕过了LeaseManager,因此您永远不必担心由于套接字异常导致对象被取消赞助,因为您首先不使用租约。但是,如果您喜欢我,这也意味着您可以在服务器上的一段时间内构建对象和非托管资源。我能看到的构建问题的唯一方法是让你的远程处理对象实现Dispose
,并确保在你完成它时处理它。基本上,你不能依赖LeaseManager处理垃圾收集,因此你必须自己做GC,这是一个很好的选择。老式的方式。
还值得注意:ITrackingHandler对象将允许您跟踪LeaseManager相关对象何时断开连接,编组和解组。这对于弄清楚发生了什么是一个很大的帮助,因为我可以看到一个对象正在被断开,而不是从呼叫停止发生的事实推断它。