Jak ponawiać operacje w .NET z wykorzystaniem Polly?

Wprowadzenia

W aplikacjach bardzo często używamy zewnętrznych zasobów. A to trzeba wykonać zapytanie na bazie danych. Pobrać dane z usługi lub wysłać wiadomość email. Zasoby te charakteryzują się tym, że mogą być przez jakiś czas niedostępne, bo na przykład wystąpił jakiś problem z siecią lub zasób jest zbyt mocno obciążony. W takich sytuacjach zastanawiamy się, czy od razu pokazać użytkownikowi informacje o błędzie, czy może spróbować ponowić operację po jakimś czasie i dopiero po któreś próbie pokazać błąd. Ten drugi sposób na ogół jest pożądanym podejściem. W dzisiejszym wpisie pokaże Ci jak łatwo i przyjemnie ponawiać operacje w momencie wystąpienia błędu (np. wyjątku) z wykorzystaniem biblioteki Polly (https://github.com/App-vNext/Polly).

Pobranie danych z Internetu

Do przetestowania biblioteki wykorzystamy klasę WebClient, która umożliwia pobierania danych z internetu. Przykład tym razem nie będzie za bardzo rozbudowany. Będzie to prosta aplikacja konsolowa, która pobierze html z głównej strony tego bloga:

Nie ma tutaj nic skomplikowanego. Aplikacja za pomocą nloga wypisuje komunikaty na konsoli (jest ona ustawiona jako target w Nlog.config). Metoda Download pobiera string z przekazanego adresu i jest wywołana w bloku try-catch, który wyświetla informacja o tym, czy udało się pobrać htmla, czy nastąpił błąd.

W normalnej sytuacji bez błędu aplikacja wyświetli coś takiego:

polly download success

Symulowanie błędów sieci

Aby przetestować w praktyce działanie Polly będziemy musieli kontrolować połączenie sieciowe. Podczas testów możemy się fizycznie odłączać od sieci, ale takie podejście na dłuższą metę jest problematyczne. Dlatego skorzystamy z gotowej aplikacji, która umożliwia symulowanie różnych problemów z działaniem sieci. Aplikacja nazywa się clumsy (https://jagt.github.io/clumsy/). Wystarczy ją pobrać, wypakować i uruchomić z uprawnieniami administratora. Co fajne nie trzeba nic konfigurować, dodawać proxy itp. Uruchamiamy i działa.

Do przetestowania Polly skorzystamy z funkcji Drop, która oznaczać zbyt długi czas odpowiedzi serwera. W testach ustawiłem, aby timeout występował z szansą 75%. Dodatkowo warto jeszcze ustawić filtr, aby aplikacja blokowa tylko żądania wysyłane do testowego serwera, a nie wszystkie połączenia w systemie. Można zrobić to za pomocą filtru na górze. Podczas pisania tego wpisu korzystałem z filtru:

Z czasem adres ip serwera, na którym jest blog może się zmienić, więc warto wcześniej to sprawdzić.

Dokładna konfiguracja clumsy wygląda tak:

polly clumsy

Po uruchomieniu i skonfigurowaniu slumsy testowa aplikacja wykona się tak (o ile oczywiście trafimy w te 75% problematycznych żądań 🙂 ):

polly download error

Darmowy kurs Visual Studio

Pracując z setkami programistów zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio, aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 20 lekcji email, w których pokaże Ci w jaki sposób pracować efektywnej i szybciej w Visual Studio. Poznasz dodatkich, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Ponawianie operacji z Polly

Biblioteka Polly (https://github.com/App-vNext/Polly) jest jedną z ciekawszych opcji do ponawiania operacji. Możemy ją wykorzystać w standardowym .NET, jak i w wersji Core, w różnych frameworkach. Sama praca z biblioteką jest prosta i składa się generalnie z trzech kroków:

  • Określenie błędu lub sytuacji, którą chcemy obsłużyć – np. wyjątek lub wynik metody
  • Zdefiniowanie co ma się stać, gdy nastąpiła sytuacja z punktu pierwszego – np. trzykrotne ponowienie operacji
  • Same wywołanie operacji, której ma dotyczyć zdefiniowana reguła

Najlepiej zobaczyć w praktyce działanie biblioteki. Poniżej jest fragment kodu, który do wcześniejszej metody pobierania html z internetu dodaje trzykrotne ponowienie operacji w momencie, gdy zostanie wyrzucony WebException:

Powyżej w kodzie widać wspomniane trzy kroki:

  • Handle rodzaj wyjątku, dla którego tworzymy regułę
  • Retry określa co ma się stać, gdy będzie wyjątek – w tym przypadku trzy krotna próba ponowienia operacji – dodatkowo dodanie do logu stosownej informacji
  • Execute uruchamia właściwą metodę, która pobiera html i do której zostanie zastosowana reguła

Zrzut ekranu, na którym widać jak zadziałała powyższe reguła. Wystąpił jeden błąd i operacja została ponowiona:

polly download retry

Ponawianie z czekaniem

Metoda Retry z wcześniejszego przykładu w momencie, gdy pojawi się błąd od razu ponawia operację. Czasami takie zachowanie jest tym, co potrzebujemy, natomiast bardzo często takie zachowanie może powodować dodatkowe problemy. W momencie, gdy serwer, do którego wysyłamy żądania jest obciążony, taki sposób ponawiania może dodatkowo obciążyć go jeszcze bardziej. Dlatego definiując reguły w Polly warto zastanowić się, czy powinniśmy od razu ponowić operację, czy może chwilkę poczekać.

Czekanie daje serwerowi czas, aby obsłużył wszystkie aktualne operacje bez dodawaniu mu kolejnych rzeczy do wykonania. Warto to rozważyć, szczególnie, że w Polly definiuje się to bardzo łatwo:

Zamiast z metody Retry, korzystamy z metody WaitAndRetry. Do metody tej przekazujemy informacje o czasie, jaki powinien minąć między poszczególnymi próbami. Czas ten możemy przekazać w postaci tablicy jak powyżej lub delegatu, który będzie obliczał kolejne okresy czekania. Poniżej wynik działania testowej aplikacji. Zwróć uwagę, że czas między poszczególnymi próbami się zwiększa:

polly download wait and retry

Ponawianie operacji w nieskończoność

Innym sposobem ponawiania operacji jest próba wykonywania jej w nieskończoność. Taki sposób ponawiania operacji też może być przydatny w aplikacji. Czasami mamy specjalną akcję w systemie, która służy do monitorowania, czy usługa lub inny zasób, z którego korzystamy jest dostępny (np. metoda Ping, która pinguje serwer co 1 minutę). W momencie, gdy serwer przestaje odpowiadać, możemy zablokować wszystkie wywołania poza tą specjalną metodą. Wywołania pinga ponawiamy w nieskończoność do momentu odpowiedzi. Gdy serwer zacznie odpowiadać możemy wznowić wszystkie operacje.

Aby dodać ponawianie w nieskończoność, wystarczy skorzystać z innej metody w regule Polly. RetryForever może próbować wykonywać operację ponownie od razu po błędzie (tak jak w przykładzie poniżej) lub możemy dodać opóźnienia między poszczególnymi próbami, tak jak w WaitAndRetry.

Kilka kolejnych prób w testowej aplikacji:

polly download retry forever

Hangfire vs Polly

Kilka tygodni temu opisywałem użycie biblioteki Hangfire (na przykładzie wysyłki email w tle). Obie biblioteki służą w pewnym sensie do ponawiania operacji. Hangfire w momencie błędu, również po jakimś czas wykonywa zadanie jeszcze raz. Kiedy więc używam Hangfire, a kiedy Polly?

Hangfire służy mi do wykonywania większych (taki bardziej biznesowych) zadań. Dobrym przykładem jest właśnie wysyłka wiadomości email. Jest to takie większe zadanie, które na ogół składa się z kilku różnych kroków. Na przykład wysłanie wiadomości potwierdzającej założenie konta (w Hangfire mamy tylko zapisane id nowego użytkownika) będzie składać się z kroków: pobranie danych z bazy o użytkowniku, wygenerowanie treści wiadomości oraz jej wysyłka.

Polly natomiast wykorzystuje do operacji niskopoziomowych, które na ogół wchodzą w skład czegoś większego. W przykładzie z wysyłką email, Polly użyłbym w dwóch miejscach: do ponawiania pobraniach danych użytkownika z bazy oraz samej wysyłki wiadomości.

Jak widać można w fajny sposób połączyć działanie tych dwóch bibliotek w jeden niezawodny system.

Przykład

Kod przykładu znajduje się w repozytorium na github: https://github.com/danielplawgo/PollyExample. Po pobraniu przykładu w metodzie Main należy zmienić wywołanie testowych metod w zależności, co chcesz testować. Pamiętaj, aby w jakiś sposób (np. za pomocą clumsy) zablokować dostęp do serwera, aby wystąpił wyjątek. Wtedy będziesz mógł sprawdzić działanie biblioteki w praktyce.

Podsumowanie

Niezawodność działania jest jednym z istotnych elementów podczas tworzenia aplikacji. Szczególnie jest to istotne, gdy coraz popularniejsze staje się rozbijanie dużych systemów na mniejsze mikro serwisy, które komunikują się z sobą. W takim środowisku ponawianie operacji jest bardzo ważne.

Dlatego warto zainteresować się biblioteką Polly, który umożliwia dodanie ponawianie w prosty i przyjemny sposób. Sama biblioteka posiada dużo większe możliwości niż to co pokazałem w tym wpisie. Dlatego gorąco zachęcam do przejrzenia jej dokumentacji na stronie https://github.com/App-vNext/Polly.

W jednym w kolejnych wpisów pokaże jak może dodać takie ponawianie transparentnie z wykorzystaniem interceptorów w autofac.

 

 

7 thoughts on “Jak ponawiać operacje w .NET z wykorzystaniem Polly?

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *