2

我有一个服务器和客户端进程都在同一台机器上运行。客户端创建一个 CAO 对象并使用它一段时间(<1 秒到几小时)。这需要大量内存,所以我想在客户端完成后尽快处理该对象。

我将 InitialLeaseTime 和 RenewOnCallTime 设置为 10s(0.1s 和 15s 有同样的问题)。我可以看到有几分钟,赞助商的 Renweal 函数每 10 秒被调用一次。几分钟后,客户开始做不同类型的工作,并且停止调用赞助商(这似乎是错误的)。几分钟后,当客户端尝试使用远程对象时,它会抛出一个异常,说它已断开连接(可能是因为很长时间没有调用赞助商)。

似乎租约经理在一段时间后以某种方式停止尝试检查租约。

4

2 回答 2

2

很长一段时间的回应,但我认为其他人也可能遇到这个问题,所以就这样吧。

我建议在 VS 中附加到您的服务器,转到“调试”菜单,选择“异常”,然后检查“System.Net.Sockets.SocketException”异常。这将在发生任何套接字异常时中断您的程序。

就我而言,我最近开始看到这个问题,经过大量调试后发现,就在租赁管理器停止检查租约之前,发生了 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 相关的对象何时断开连接、编组和取消编组。这对弄清楚发生了什么很有帮助,因为我可以看到一个对象正在断开连接,而不是从调用停止发生的事实中推断出来。

于 2016-06-01T14:03:01.717 回答
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() 的情况下异常退出,那么该对象将永远保持活动状态。但在我的情况下,服务器是由客户端启动和停止的,所以这并不重要。

于 2013-09-23T01:26:47.387 回答