Hangfire – wysyłka email w tle

W poprzednim wpisie pokazałem jak w aplikacji ASP.NET MVC wysyłać wiadomości email z wykorzystaniem biblioteki Postal. Wspomniałem również, że wysyłka email w ramach żądania HTTP nie jest dobrym pomysłem, że lepiej skorzystać z jakiego mechanizmu kolejek oraz ponawiania operacji. Jednym z dostępnych narzędzi jest biblioteka Hangfire, która w prosty sposób jest wstanie wysłać email w tle, a do tego ponowić operację w momencie wystąpienia błędu.

Hangfire

Ostatnio do kolejkowania zadań oraz wykonywania ich w tle wykorzystuje bibliotekę Hangfire (https://www.hangfire.io/). Lubię ją z kilku powodów:

  • Jest darmowa, więc nie muszę płacić – jest również wersja płatna z kilkoma ciekawymi funkcjonalnościami, ale na ogół ich nie potrzebuje
  • Bardzo łatwa integracja z aplikacją ASP.NET MVC – w praktyce wystarczy dodać kilka linijek w Startup.cs, aby korzystać z zadań wykonywanych w tle
  • Fajny dashboard z informacjami o wykonywanych zadaniach
  • Możliwość wykonywania zadań na różnych komputerach, więc relatywnie łatwo można skalować wykonywanie zadań na kilka maszyn
  • Jest elastyczne w wykorzystywaniu poszczególnych elementów biblioteki, zadania mogę być wykonywane w zupełnie innej aplikacji niż są dodawane, podobnie z dashboardem
  • Wersja płatna jest relatywnie tania – 500$ za organizacje (nie programistę jak to była na ogół) za rok

Hangfire instalacja oraz konfiguracja

Standardowo w pierwszej kolejności instalujemy pakiet Hangfire (https://www.nuget.org/packages/HangFire). Domyślnie Hangfire korzysta z bazy danych MS Sql Server do przechowywania informacji o zadaniach. W wersji płatnej możemy skorzystać z Redis. Jak podają twórcy biblioteki, implementacja z wykorzystaniem Redisa jest około 4 razy szybsza niż wykorzystywanie bazy danych.

W przypadku MS Sql Servera możemy użyć tej samej bazy, w której normalnie przechowujemy dane lub zdecydować się na dedykowaną bazę dla biblioteki. W przykładzie dodałem connection stringa do web.config:

Mając skonfigurowaną źródło przechowywania danych możemy skonfigurować już samą bibliotekę. Wszystko robi się w pliku Startup.cs:

Konfiguracja składa się z wywołania trzech metod:

  • UseSqlServerStorage – informuje, że korzystamy z bazy MS Sql Server do przechowywania informacji oraz przekazuje nazwę connection stringa, którego ma użyć biblioteka
  • UseHangfireDashboard – informuje, że w aplikacji ma być dostępny dashboard z informacjami o zadaniach
  • UseHangfireServer – informuje, że w ramach aplikacji mają działać workery, które będą wykonywać zadania

Jak wspomniałem wcześniej, poszczególne elementy biblioteki możemy zaimplementować w różnych aplikacjach. Na przykład możemy mieć jedną aplikację z dashboardami z wszystkich aplikacji, gdzie wykorzystujemy Hangfire. Aplikacja może działać tylko w sieci lokalnej firmy i nie ma do niej dostępu z internetu.

Dodanie zadania do wykonania

Po skonfigurowaniu Hangfire samo dodanie zadania do wykonania jest bardzo proste. Wystarczy zamienić wywołanie właściwej metody (np. SendRegisterEmail z klasy UserMailer) na wywołanie statycznej metody Enqueue z klasy BackgroundJob (są też inne sposoby, ale o tym w późniejszych wpisach), gdzie w parametrze przekazujemy w formie lamby wcześniejsze wywołanie. Poniżej jest metoda z logiki biznesowej pokazująca dodanie zadania wysłania wiadomości email. W komentarzu jest również wcześniejsza wersja dla porównania.

Wykonanie zadania oraz dashboard

Mając już zmieniony kod możemy spróbować dodać nowego użytkownika, aby system wysłał wiadomość email z wykorzystaniem Hangfire. Po dodaniu zadania pojawi się ono na liście w dashboardzie. Domyślnie dashboard dostępny jest pod adresem [adres aplikacji]/hangfire. W dashboardzie możemy zobaczyć listę zadań z podziałem na statusy (do wykonania, wykonywane, zakończone itp.), czy też możemy zobaczyć szczegóły zadania (zserializowane dane zadania, informacja o ilości wykonywań, czy błędy, gdy nie udało się wykonać zadania).

Hangfire lista zadań

Hangfire szczegóły zadania

Hangfire, Postal oraz kontener DI (Autofac)

Na koniec jeszcze jedna istotna informacja na temat integracji Hangfire, Postal oraz kontenere Dependency Injection, w moim przypadku Autofac. Niestety kiedy w aplikacji chcecie połączyć te trzy biblioteki (podobnie może być z innymi kontenerami, które wspierają cykl życia obiektu per żądanie HTTP), podczas wykonywania zadania z wysyłką wiadomości dostaniecie błąd, że wymagany jest kontekst HTTP do utworzenia jakiegoś obiektu. Widać na zrzucie zadania poniżej (co fajne, po poprawieniu błędy zadanie zostało poprawnie wykonane i wiadomość została wysłana w jednej z kolejnych prób, co pokazuje, że Hangfire działa 🙂 ):

Hangfire błąd z zadaniu

Takie działanie aplikacji spowodowane jest tym, że klasa ViewEngineCollection, którą wykorzystujemy w metodzie Send z klasy BaseMailer, w swoim kodzie korzysta z ustawionego w ASP.NET MVC resolvera (czyli w moim przypadku skonfigurowanego kontenera Autofac). Próbuje za jego pomocą wyciągnąć obiekty wewnętrzne z silnika, a one mają ustawiony cykl życia per żądanie. Co fajne, tamte obiekty nie są potrzebne do poprawnej wygenerowania treści wiadomości. Jednym z rozwiązań jest stworzenie własnej klasy dziedziczącej po ViewEngineCollection i w konstruktorze za pomocą refleksji zmianę wykorzystywanego resolvera, aby klasa nie korzysta z kontenera. Poniżej znajduje się zmieniona klasa BaseMailer, które właśnie wykorzystuje zmienioną klasę ViewEngineCollection. W przypadku, gdy nie korzystamy z kontenera, zmiana wcześniejszego kodu nie jest potrzebna.

Podsumowanie

Dodanie wykonywania zadań z wykorzystaniem kolejek nie musi być bardzo ciężkie. Dzięki bibliotece Hangfire może być to wręcz łatwe i przyjemne. Jak widać daje to dużą niezawodność w działaniu aplikacji, poprzez możliwość ponawiania wykonywania zadań, które się nie udało. Dodatkowo przejrzysty dashboard daje dużo informacji o tym, co dzieje się z zadaniami w aplikacji.

Testowy projekt tradycyjnie znajdziesz na moim github. Zapraszam do pobrania i przetestowania w praktyce bibliotekę Hangfire.

A Tobie jak podoba się Hangfire? Znałeś wcześniej tą bibliotekę? A może używasz czegoś fajniejszego? Daj znać w komentarzu.

 

5 thoughts on “Hangfire – wysyłka email w tle

  • Pingback: dotnetomaniak.pl
  • Hej, jak wygląda to w przypadku gdy IIS wyłączy naszą aplikację po 20 minutach bezczynności?
    Hangfire dalej będzie wykonywać swoje taski?

  • Cześć a kojarzysz może jak sprawdzić czy dane zadanie jest odpalone np: w kontrolerze? Rozumiem, że sam job może dawać informację do kogoś, że jest odpalony ale czy działa to w druga stronę.
    Sprawdzam czy dany job jest odpalony.

    Używam tego od roku. Biblioteka się dobrze sprawuje i jest stabilna. Szkoda tylko, że kliku elementów jej brakuje np: nie ma możliwość skonfigurowania aby job się zrobił się tylko raz. Z poziomu dashboard nie można wyłączyć job-a i z tego co wiem nie ma jak przeglądać historii job-a. Można zobaczyć ostatnie wywołanie ale nie całej historii.

  • Hej, do końca nie rozumiem pytania. Chodzi Ci o to, aby wiedzieć, czy dana metoda, która ma się wykonać jest uruchomiona z kontrolera A lub B? Można chyba przekazać jakieś parametr, który zostanie zserializowany i później to wykorzystać.

    Ja to czasami opakowuje jakieś metody, aby tą somą logikę wykonać w innym kontekście (np. innej kolejce w Hangfire – przykład w demie z prezentacji o Hangfire – https://github.com/danielplawgo/HangfireDemo/blob/master/HangfireDemo/HangfireDemo.Logic/ParserJobs/ParserJobsLogic.cs#L127)

Dodaj komentarz

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