2013-08-22 14 views
9

Tło:
Mam kod, który łączy się z serwerem w celu pobrania danych. Jednak w pewnym momencie program znalazł się w stanie, w którym nie mógł połączyć się z serwerem. Problemem, który obserwowaliśmy, jest to, że serwer nieustannie limitował czas, kiedy próbowaliśmy ponownie się połączyć.Zamknij wszystkie podstawowe połączenia sieciowe

Plugin:
Posiadamy Timeout ustawiony na wartości domyślne używane przez nasze wtyczki sieciowej (plugin Thrift). Te wartości to limit czasu 100k milisekund i 300k milisekund czasu oczekiwania na odczyt. Niezupełnie krótkie limity czasu. Za każdym razem, gdy próbujemy ponownie się połączyć, odtwarzamy klasę wtyczki, więc wszelkie wartości, które ustawia wewnętrznie, są resetowane za każdym razem, gdy próbujemy się połączyć. Uważam, że czasy oczekiwania nie są problemem. I wierzę, że wtyczka nie jest problemem.

Sieć:
Widzieliśmy, że te powtarzające się limity czasu występują przez pół godziny. Kiedy ponownie uruchomiliśmy usługę (nie maszynę), natychmiast wrócił on-line. Wiem, że to nie był problem z siecią. To prowadzi mnie do przekonania, że ​​jest to coś w naszym kodzie, który dostaje się do nieprawidłowego stanu sieci. Poza tym jest to stan, którego nie możemy kontrolować, ponieważ nasza wtyczka ukrywa przed nami wszystkie dobre rzeczy sieciowe - w tym limity czasu, flagi keepalive i grupy połączeń (między innymi). Zasadniczo nie mamy dostępu do członka HTTPWebReqest.

Pytanie:
Kiedy nasze ustawienia sieci dostać się do tego stanu, staramy się, aby zamknąć wszystkie połączenia i ponownie połączyć się z serwerem. Celem było zabicie wszystkich aktywnych połączeń w celu zresetowania dowolnego stanu, w którym się znaleźliśmy. Jednakże, gdy próbujemy ponownie się połączyć, otrzymujemy limity czasu na ponowne połączenie.

W jakiś sposób (prawdopodobnie ze względu na KeepAlive i TCP Pipelining, których nie możemy kontrolować), połączenia sieciowe pozostają otwarte, mimo że wszystkie połączenia zostały zamknięte. To pozostawia nas w złym stanie i uniemożliwia nam odbieranie połączeń z serwerem.

Pytanie:
Jak mogę zabić wszystkie połączenia bazowych do serwera w celu wymuszenia ponownego połączenia?

+7

Osobiście polecam zmianę biblioteki, której używasz. Ponieważ jednak jestem tobą, wiem, że to nie jest opcja. – Richard

+0

+1. Miły komentarz własny i odpowiedź – Danexxtone

Odpowiedz

9

Podstawowa architektura .Net będzie utrzymywać połączenie (przez KeepAlive) w celu poprawy ogólnej wydajności sieci. Najprostszym rozwiązaniem jest ustawienie wartości KeepAlive na wartość false dla wszystkich połączeń, aby nie utrzymywały tych nieprawidłowych stanów. Minusem jest to, że zwiększysz ruch, ponieważ za każdym razem będą musieli ponownie nawiązać połączenia.

Ponieważ użytkownik nie ma dostępu do obiektu keepalive (lub obiektu HTTPWebRequest), należy znaleźć sposób na zniszczenie połączeń poza ustawieniami żądania.

Istnieją dwa podejścia, które można podjąć, aby uzyskać .Net, aby zwolnić te połączenia. Oba dotyczą podejścia do poziomu ServicePoint i zajmowania się grupami połączeń.

  1. CloseConnectionGroups

    podczas tworzenia HttpWebRequest, można nadać jej grupy przyłączeniowej. Stamtąd możesz użyć funkcji CloseConnectionGroup() z ServicePoint, aby zabić wszystkie połączenia z tą usługą.

    ServicePoint srvrPoint = ServicePointManager.FindServicePoint(serverUri); 
    srvrPoint.CloseConnectionGroup(myConnGroup) 
    

    Oczywiście, jeśli masz więcej niż jedną grupę połączeń, musisz to zrobić w pętli dla każdej grupy połączeń.

    Pamiętaj, że jeśli wybierzesz tę ścieżkę, nie umieszczaj wszystkich połączeń w tej samej grupie. Może to spowodować wyczerpanie gniazd. More information here.

  2. ReleaseAllConnectionGroups

    Istnieje wewnętrzna funkcja w klasie Servicepoint które można nazwać, że zabije wszystkich połączeń do punktu serwisowego, otrzymując (czyli serwera). Jednak, ponieważ jest to wewnętrzna do klasy, trzeba będzie użyć refleksji uzyskać na to:

    ServicePoint srvrPoint = ServicePointManager.FindServicePoint(serverUri); 
        MethodInfo ReleaseConns = srvrPoint.GetType().GetMethod 
          ("ReleaseAllConnectionGroups", 
          BindingFlags.Instance | BindingFlags.NonPublic); 
        ReleaseConns.Invoke(srvrPoint, null); 
    

    Ten kod po prostu wyciąga ReleaseAllConnectionGroups z tego ServerPoint i wywołuje go bez parametrów. Ta funkcja przejdzie przez wszystkie grupy połączeń na swojej wewnętrznej liście i zwolni wszystkie te zasoby - zabijając wszystkie połączenia.

    Upadek: ponieważ jest to członek prywatny, nie można zagwarantować, że będzie dostępny po uaktualnieniu wersji .Net. Obecnie jest dostępny w wersjach od 2.0 do 4.5. Link do the reverse engineered code of ReleaseAllConnectionGroups.

Bo nie można ustawić grupę połączenia, będziesz musiał użyć opcji 2.

Niestety, nie ma innego sposobu, aby dostać .Net do zamknięcia tych połączeń niskopoziomowych i uwolnienia ich podtrzymywanie status bez ustawiania flagi KeepAlive.

+1

Po spędzeniu dużej ilości czasu na naprawieniu problemu natrafiłem na to rozwiązanie. To działało jak urok dla mnie. Dzięki –

0

Dlaczego nie użyć kodu folowing:

SqlConnection.ClearAllPools();

Używam go w moich aplikacjach.

Powiązane problemy