2014-06-12 11 views
5

Mamy C# serwis internetowy i klienta, oba utworzone w Visual Studio 2008 (nowy projekt -> usługa sieci Web ASP.Net). Usługa jest hostowana na serwerze Windows 2012 R2, IIS 8.5.Niewystarczające zasoby winsock

Kiedy klient wysyła dane do naszej usługi, przekazujemy je do usługi zewnętrznej, zapisujemy wynik w bazie danych i zwracamy do klienta.

Problem polega na tym, że w niektórych rzadkich przypadkach, gdy nasza usługa jest obciążona dużym obciążeniem (wiele żądań na sekundę), rozpoczyna się wyrzucanie "Niewystarczających zasobów systemu winsock dostępnych do zakończenia inicjowania połączenia z gniazdem".

Okazało się, że nasz serwis internetowy otwiera wiele połączeń TCP z usługami innych firm i pozostawia je w stanie TIME_WAIT. Kiedy liczba takich połączeń osiąga dużą liczbę (około 17000), cały serwer traci możliwość tworzenia nowych połączeń. Wszystko od zdalnego pulpitu do przeglądarki internetowej przestaje działać. Trwa to kilka minut, a następnie, gdy Windows rozpoczyna zamykanie tych połączeń, wznawia normalnie.

W celu komunikacji z usługą innej firmy nasza usługa wykorzystuje tylko jedną instancję SoapClient przez cały czas jej użytkowania. Jest tworzony podczas inicjalizacji i nigdy nie jest zamknięty ani niszczony; nowe instancje nigdy nie są tworzone.

BLIND.BLINDSoapClient client = new BLIND.BLINDSoapClient(base.binding, base.address); 

Wysyłając dane do usługi 3rd party po prostu wywołać jego metodę WWW i pozostawić ją tak bez zamykania, usuwania lub wykonując dowolną clean-up:

BLIND.Answer answer = client.Search(...); 
..save to database 
return answer; 

Czy coś nam może zrobić, aby uniknąć gromadzenia się połączeń time_wait?

Czy istnieje lepszy sposób zarządzania SoapClient (s)? Czy powinniśmy otworzyć nowego klienta mydła dla każdego żądania i zamknąć je ręcznie?

Jeśli jest to istotne, tutaj jest jak nasz wiążący jest skonfigurowana:

 binding = new BasicHttpBinding(); 
     binding.Name = "SLTDSoap"; 
     binding.CloseTimeout = TimeSpan.FromSeconds(Timeout); 
     binding.OpenTimeout = TimeSpan.FromSeconds(Timeout); 
     binding.ReceiveTimeout = TimeSpan.FromSeconds(Timeout); 
     binding.SendTimeout = TimeSpan.FromSeconds(Timeout); 
     binding.AllowCookies = false; 
     binding.BypassProxyOnLocal = false; 
     binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard; 
     binding.MaxBufferSize = 65536; 
     binding.MaxBufferPoolSize = 524288; 
     binding.MessageEncoding = WSMessageEncoding.Text; 
     binding.TextEncoding = System.Text.Encoding.UTF8; 
     binding.TransferMode = TransferMode.Buffered; 
     binding.UseDefaultWebProxy = true; 

     binding.ReaderQuotas.MaxDepth = 32; 
     binding.ReaderQuotas.MaxStringContentLength = 8192; 
     binding.ReaderQuotas.MaxArrayLength = 16384; 
     binding.ReaderQuotas.MaxBytesPerRead = 4096; 
     binding.ReaderQuotas.MaxNameTableCharCount = 16384; 

     binding.Security.Mode = (_url.StartsWith("https:")) ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None; 
     binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; 
     binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None; 
     binding.Security.Transport.Realm = ""; 
     binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; 
     binding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default; 

System.Net.ServicePointManager.DefaultConnectionLimit = 500; 

Dziękujemy!

+0

Jest to klasa stworzona przez studio graficzne po dodaniu odwołania do usługi internetowej innej firmy. Kliknięcie prawym przyciskiem myszy Odsyłacze do usług -> Dodaj odwołanie do usługi – dbrckovi

Odpowiedz

4

Myślę, że moglibyśmy rozwiązać problem "niewystarczających zasobów systemu winsock".

Mamy ustawić następujące wartości rejestru: HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ Tcpip \ Parameters \ MaxUserPort = 60000 HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ Tcpip \ Parameters \ TcpTimedWaitDelay = 30

Nasza maksymalna spodziewane obciążenie środowiska produkcyjnego w godzinach podglądu wynosi 150 żądań na sekundę. Oznacza to, że utworzymy 4500 połączeń w ciągu 30 sekund, zanim Windows zacznie je zwalniać. Jest to znacznie poniżej 60000 i powinno zapewnić, że problem nie powtórzy się.

Zostawiliśmy system uruchomiony z 150 żądaniami na sekundę przy tych ustawieniach przez 3 dni, a problem nie wystąpił.

0

Według uznania BLIND.BLINDSoapClient dziedziczy po System.ServiceModel.ClientBase. Ta klasa to IDisposable, co oznacza, że ​​powinieneś ją wyrzucić zawsze, gdy skończysz (na tle wywołuje Close - zamyka obiekt komunikacyjny w tle).

tj .:

using(var client = new BLIND.BLINDSoapClient(base.binding, base.address)) { 
    // enjoy client 
} 

Tam ostatecznie mogą być pewne ograniczenia, które mogą wziąć swój serwer w dół. Można:

  1. Ustaw ograniczenie liczby wniosków dopuszczonych do swojej witryny/usługa IIS - patrz http://www.iis.net/configreference/system.applicationhost/sites/sitedefaults/limits pod dużym obciążeniem ty nazywa się usługa będzie wtedy czasami nie - ale po stronie klienta
  2. Najedź usługa od jedno pudełko do rozwiązania farmy internetowej (jedno pudełko działa jako moduł równoważenia obciążenia, który przekazuje żądania do wielu skrzynek w oparciu o ich bieżące obciążenie)
  3. Przenieś swoją usługę do chmury - jak Amazon lub Azure (to samo 2., tylko Ty nie musisz przejmować się balansem obciążenia)
+0

Nie możemy usunąć BLIND.BLINDSoapClient, ponieważ używamy tego samego obiektu do komunikacji z usługami innych firm. Jeśli go wyrzucę, kolejne żądanie zakończy się niepowodzeniem, ponieważ klient zostanie zamknięty. W takim przypadku będziemy musieli utworzyć nowe wystąpienie tej klasy dla każdego żądania. Czy to jest lepsza praktyka? – dbrckovi

+0

Na marginesie, planujemy przenieść usługę do chmury w pewnym momencie, ale naszym priorytetem jest teraz wyeliminowanie wszystkich problemów, które mogą być spowodowane złym wzornictwem. – dbrckovi

+0

@ user3733031 - lepiej jest tworzyć nową instancję, gdy jej potrzebujesz i usuwać ją, gdy skończysz. W przeciwnym razie połączenie pozostanie otwarte przez cały czas trwania żądania internetowego, a nie będzie otwarte tylko wtedy, gdy będzie ono potrzebne. –

Powiązane problemy