Функция обновления спонсора перестает вызываться

У меня есть сервер и клиентский процесс, работающий на одной машине. Клиент создает объект CAO и использует его в течение некоторого времени (<от 1 до часов). Это занимает много памяти, поэтому я хочу избавиться от этого объекта как можно скорее после того, как клиент закончит с ним.

Я установил InitialLeaseTime и RenewOnCallTime на 10 с (0,1 с и 15 с имеют ту же проблему). Я вижу, что в течение нескольких минут функция спонсора Renweal вызывается каждые 10 секунд. Через несколько минут клиент начинает выполнять другую работу, и спонсор перестает звонить (это кажется неправильным). Через несколько минут, когда клиент пытается использовать удаленный объект, он генерирует исключение, сообщающее, что он был отключен (возможно, потому, что спонсор не вызывался в течение длительного времени).

Кажется, что менеджер по аренде как-то перестает пытаться проверить аренду через некоторое время.

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 - если у вас их два или более, возможно, это событие вызвано переключением между ними), которое, по-видимому, приводит к прекращению чтения сокета. Это означало, что в следующий раз, когда Lease Manager использовал удаленное подключение для проверки удаленной аренды, он не мог прочитать эту аренду из мертвого сокета. Таким образом, он сделал разумную вещь - отключил этого спонсора, так как мы больше не можем его читать, и удалил его из списка спонсоров для объекта. И, поскольку он, вероятно, является единственным спонсором рассматриваемого объекта, этот объект не получает спонсорства от Lease Manger, что дает GC возможность в конечном итоге забрать его.

Один из подходов к решению этой проблемы заключается в InitializeLifetimeService() метод, вернуть ноль вместо установки времени ожидания. Это обходит LeaseManager, поэтому вам никогда не придется беспокоиться о том, что объект не будет спонсироваться из-за исключения сокета, так как вы в первую очередь не используете аренду. Однако, если вы похожи на меня, это также означает, что вы могли бы накапливать объекты и неуправляемые ресурсы в течение определенного периода времени на сервере. Единственный способ обойти проблему с наращиванием, которую я вижу, это заставить ваш удаленный объект реализовать Disposeи убедившись, что вы утилизируете его, когда закончите с ним. В принципе, вы не можете полагаться на LeaseManager, обрабатывающий сборку мусора, поэтому вам придется делать GC самостоятельно, по старинке.

Также стоит отметить: объект ITrackingHandler позволит вам отслеживать, когда связанные с Lease Manager объекты отключены, маршалированы и не маршалированы. Это очень помогло выяснить, что происходит, так как я мог видеть, что объект отключался, а не выводить его из-за того, что вызовы перестали происходить.

Я решил эту проблему, разместив спонсора на сервере, а не на клиенте. Спонсор серверной стороны, кажется, достаточно надежно вызван, чтобы поддерживать удаленный объект в живых.

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(), тогда объект останется живым навсегда. Но в моем случае сервер запускается и останавливается клиентом, так что это не имеет значения.

Другие вопросы по тегам