Jak dostosować ilość requestów do ograniczeń serwera?

0

Natrafiłem na następujący problem. Serwer z którym się komunikuję ogranicza ilość przyjmowanych requestów do 10 na sekundę. Mam ich do wysłania tysiąc i więcej. Z każdego robię osobny Task i pcham je asynchronicznie. Oczywiście po chwili serwer mi się blokuje i zaczyna zwracać błędy. Co wtedy? Jedyne co przyszło mi do głowy to zrobić pułapkę catch wyłapującą System.Net.WebException i po odczekaniu sekundy wysyłać request ponownie. Efekt: dostaję odpowiedź na co 20 wysłany request (choć i tak jest szybciej niż gdyby robić to synchronicznie). Pewnie jest jakaś elegancka metoda, żeby to zgrabnie uregulować...Help.

0

Może trzymaj w kolejce 10 ostatnich czasów wysłania requestu i za każdym razem gdy chcesz wysłać kolejny

  1. ściągaj najdawniejszy czas z kolejki
  2. odczekaj tyle, żeby minęła co najmniej sekunda od tego czasu
    do tego dodatkowe oczekiwanie jeśli wystąpi błąd
0

No tak właśnie teraz kombinuję. Zrobiłem sobie licznik od 0 do 10 i gdy dojeżdża do 10 to na 2 sekundy wstrzymuję się z wysyłaniem requestów.
Tyle, że gdy steruję odpalaniem tasków w wątku głównym, to blokuję na kilka minut działanie aplikacji.

0

Słabo. Działa dopóki serwer nie spóźni się z odpowiedzią, wtedy requesty do ponownego wysłania zaczynają się kumulować i jest efekt śnieżnej kuli. Biorę się do Twojej sugestii z kolejką czasów wysłania 10 ostatnich requestów. Tylko jak ugryźć równoczesny dostęp do zmiennej przez 10 tasków równocześnie?

1

Zobacz bibliotekę o nazwie Polly :) myślę że rozwiąże twój problem. Pozwala ona np. na coś takiego

RetryPolicy retry = Policy
  .Handle<HttpException>()
  .Retry(3);
Policy
  .Handle<SomeExceptionType>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(2),
    TimeSpan.FromSeconds(4),
    TimeSpan.FromSeconds(8),
    TimeSpan.FromSeconds(15),
    TimeSpan.FromSeconds(30)
  });
1

Jeśli zdecydujesz się na moje rozwiązanie to możesz użyć ConcurrentQueue

0

Dziękuję Panowie. Już donoszę z jakim efektem zastosowałem Wasze porady.
Pół nocy zarwałem, na próby ogarnięcia tematu z użyciem ConcurrentQueue gdzie zapisywałem czas wysyłania kolejnych requestów, ale niezmiennie wychodziła mi jakaś partyzantka (Obrona Terytorialna?). Nie wiem jak wewnętrznie działa ConcurrentQueue, ale chyba poszczególne wątki wrzucają do niej elementy równocześnie, więc limitowanie częstotliwości słania requestów za jej pomocą to kwadratura koła. Jedyne co mogłem zrobić to limitować co jaki czas mają być one wysyłane, a i tak było to na nic bo nawet jak odczekałem, system rozpoczynał mi naraz 10 threadów naraz i miałem ten sam problem co zawsze.

Patrzyłem też na Polly. Dobre, Ale bardziej do obsługi poszczególnego wątku w sytuacji, gdy połączenie z serwerem jest kiepskie. Gdy zarzucę serwer zapytaniami to zapcha się, niezależnie od tego ile odczekam z ponownym wysłaniem tego samego requesta bo wciąż będą wpadać nowe.

I wtedy znalazłem coś takiego jak class ParallelOptions z właściwością MaxDegreeOfParallelism, pozwalającą na kontrolowanie ilości wątków które system będzie wykonywał. Świetna sprawa. Dotyczy co prawda Action a nie Task, ale to żaden problem. Zapakowałem wszystkie Action na jedną listę. Ustaliłem ilość równoczesnych wątków. I heja.

var options = new ParallelOptions {MaxDegreeOfParallelism = 3};
    Parallel.Invoke(options, listaActions.ToArray());

Pewnie to odkrywanie przeze mnie Ameryki 500 lat po Kolumbie, ale tym nie mniej...

0

Jeśli serwer zwraca jakąś informację zwrotną w przypadku sukcesu to może ustawić jakąś zmienną informującą ile zapytań wisi aktualnie na serwerze ? I po prostu po wykonaniu odejmujesz o 1 z tej wartości a wysłanie zapytania dodaje 1.

1 użytkowników online, w tym zalogowanych: 0, gości: 1