Cześć! Czas na kolejny wpis w Cesarstswie-Dev. Ostatnie wpisy były skupione na wysokopoziomowym opisie architektury systemów. Jeśli nie miałeś okazji zapoznać się z przebieżką po architekturach, to z przyjemnością mogę Cię do tego zaprosić. Dzisiaj zejdziemy do poziomu implementacji i skupimy się na konkretnym rozwiązaniu technicznym. W związku z tym opowiemy sobie o Azure Functions! Zaczniemy od krótkiego opisania podstaw tego zagadnienia, a następnie opowiemy sobie o plusach i minusach rozwiązania opartego na przetwarzaniu bezserwerowym. W tym wpisie zapoznamy się również z przykładową architekturą korzystającą z Azure Functions, jak i dowiemy się jak je zaimplementować. Raz, dwa trzy – zaczynamy!

https://www.c-sharpcorner.com/UploadFile/BlogImages/11182019033745AM/AWS%20Lambda%20vs%20Azure%20Functions%20Which%20Should%20You%20Opt%20For1.png

Bezserwerowe, czyli jakie?

Przetwarzanie bezserwerowe stało się bardzo popularne w krótkim okresie czasu. Serverless jest wszędzie i każdy o nim mówi. Najpopularniejsi dostawcy chmury publicznej udostępniają go w swojej ofercie. Tak więc Microsoft daje nam Azure Functions, Google udostępnia Google Cloud Functions, natomiast Amazon oferuje AWS Lambda. Takie rozwiązanie pozwala programistom tworzyć kod, przynoszący wartość biznesową bez konieczności martwienia się infrastrukturą. Pojęcie „bezserwerowe” potrafi być mylące, ponieważ bynajmniej nie odnosi się do braku serwerów. Przymiotnik ten opisuje przeniesienie odpowiedniości za zarządzanie nimi na dostawcę usługi w chmurze.

Gdybym miał opisać przetwarzanie bezserwerowe własnymi słowami to powiedziałbym, że są to zaimplementowane funkcje, które uruchamiane są na dynamicznie przydzielonym serwerze pod wpływem pewnego zdarzenia. O różnych możliwościach wyzwalania Azure Functions porozmawiamy w rozdziale związanym z implementacją. Taki rodzaj wykorzystania chmury obliczeniowej nazywany jest FaaS (Function as a service). Definicja mówi, że jest to rodzaj platformy, która umożliwia klientom możliwość implementacji, uruchamiania oraz zarządzenia funkcjami aplikacji bez konieczności budowania i zarządzania infrastrukturą.

Opiszmy teraz samą funkcję! Programista, który chce wykorzystać przetwarzanie bezserwerowe, tworzy kod funkcji na podstawie szablonu udostępnionego przed dostawcę. Sam szkielet zapewnia mechanizm uruchamiania oraz różne definicje wyzwalaczy (ang. triggers). Po wdrożeniu funkcja wyczekuje na ustalone przez trigger zdarzenie. Mechanizm, który obsługuje nasze oprogramowanie, w chwili otrzymania odpowiedniego zdarzenia uruchamia kontener oraz przesyła do niego zapytanie. Po przetworzeniu żądania kontener odczekuje jeszcze krótką chwilę (w celu szybkiej obsługi kolejnego zdarzenia), by następnie się zatrzymać. Taki sposób działania wymusza bezstanowość funkcji bezserwerowych.

Wady i zalety

Wszystko brzmi pięknie! Od tego też zacznijmy wymienianie cech przetwarzania bezserwerowego (skupiając się głównie na Azure Functions). Największą zaletą takich funkcji jest ich ogromna skalowalność. Brak odgórnie przydzielonych serwerów pozwala dynamicznie przydzielać zasoby w czasie dowolnego obciążenia. Co więcej, podczas zerowego obciążenia nie zabierają one żadnych zasobów! To sprowadza nas do drugiej zalety – efektywnego używania zasobów. Kolejnym punktem, który już kilkakrotnie podkreślałem w tym wpisie, jest brak konieczności zarządzania infrastrukturą. Pozwala to skupić się nam, programistom, wyłącznie na dostarczaniu wartości biznesowej. Ostatnia (wymieniona w tym wpisie) zaleta jest mniej oczywista. W związku z potrzebą pisania wyłącznie kodu biznesowego czas wprowadzania aplikacji na rynek znacząco się skraca! Razem z tym skraca się również czas oczekiwania na sprężenie zwrotne od klientów, jak i czas trwania wdrożenia, co świetnie współgra ze zwinnymi metodykami tworzenia oprogramowania. Dodatkowo, funkcje bezserwerowe świetnie nadają się do implementacji PoC (ang. Proof of concept).

Wydaje się, że przetwarzanie bezserwerowe to Święty Graal oprogramowania! Cóż więcej potrzebujemy, niż dynamicznie skalowalnych, oszczędnych oraz ułatwiających implementacje rozwiązań? Niestety, nic nie jest idealnie. W związku z tym przejdźmy do wad tego rozwiązania. Po pierwsze, dynamiczna skalowalność jest ograniczona do zasobów naszego portfela. Co więcej, źle skonfigurowane skalowanie może nas niemiło zaskoczyć przy miesięcznym rozliczeniu. Trzeba ostrożnie korzystać z możliwości, jakie oferują nam dostawcy chmur. Niedoświadczony użytkownik może łatwo się skaleczyć z powodu braku pełnej transparentności kosztów. Warto również wspomnieć o bezpieczeństwie, które uzależnione jest od dostawcy usługi. Nasze oprogramowanie nie jest już związane z konkretnymi serwerami, więc musimy poradzić sobie z zabezpieczeniem dynamicznego środowiska. Kolejnym aspektem będzie uzależnienie od wybranego dostawcy chmury. Kod implementujący przetwarzanie bezserwerowe jest bezpośrednio związany z szablonem dostarczonym właśnie przez niego. Zmiana dostawcy w czasie działania systemu jest nie tylko czasochłonna, ale i kosztowna.

Przykładowa architektura

Miałem przyjemność użyć Azure Functions w kilku projektach. Jednym z nich była moja praca inżynierska: wykrywanie pozycji człowieka (stoi/leży) za pomocą czujnika GridEye oraz chmury Azure. Nie chcę wchodzić w szczegóły rozwiązania, więc w maksymalnym skrócie opowiem, na czym polega problem. GridEye jest czujnikiem temperatury komunikującym się za pomocą protokołu I²C. System powinien pobierać dane z zamontowanego w pomieszczeniu czujnika GridEye połączonego z Raspberry Pi, a następnie powiadamiać o zmianach zewnętrzne aplikacje. Schemat architektury znajduje się na poniżej.

Z lewej strony widzimy hardwareową część rozwiązania. W celu przesłania danych z urządzenia do chmury wykorzystałem Azure IoT Hub. W chwili otrzymania cyklicznie wysyłanego obrazu pomieszczenia IoT Hub wyzwalał funkcję przekazując jej dane z czujnika. Kolejnym etapem jest wyznaczenie ilości ludzi znajdujących się w przesłanym obrazie oraz ustalenie ich pozycji. W tym celu funkcja korzysta z magazynu danych (Azure Storage). Przy wykryciu zmian w liczbie, bądź pozycji, ludzi na obrazie, funkcja emituje odpowiednie zdarzenie. Ostatecznie trafia ono na kolejkę kolejnej funkcji (Indexing Function), której celem jest pobieranie wiadomości z kolejki oraz zapisywanie ich w bazie NoSql.

Zastosowanie przetwarzania bezserwerowego w projekcie IoT jest bardzo dobrym pomysłem. Pozwala to na dynamiczne skalowanie rozwiązania, a więc na podpięcie większej liczby czujników (w różnych pomieszczeniach). Częstym zastosowaniem Azure Functions jest przetwarzania wiadomości z kolejek i zapisywanie ich w bazie. Dzięki takiemu podejściu sami decydujemy o obciążeniu bazy (konfigurując skalowalność odpowiedniej funkcji), jak i pozwalamy na stabilne działanie systemu w czasie niedostępności wspominanej bazy.

Implementacja Azure Functions

Czas na programowanie! Zacznijmy od stworzenia odpowiedniego projektu.

Po wybraniu odpowiedniego typu zostaniemy także poproszeni o wybór triggera:

Zaczniemy od HttpTrigera! Po stworzeniu projektu naszym oczom ukaże się kod funkcji bezserwerowej, która gotowa już jest do uruchomienia. Microsoft udostępnia nam emulator, który pozwala na lokalne uruchamianie oraz debuggowanie Azure Functions! Najpierw jednak przeanalizujmy wygenerowany program.

Wygenerowana została statyczna klasa zawierająca jedną metodę. Należy zwrócić uwagę na atrybut FunctionName, którym oznaczona musi być każda funkcja. W parametrach tej metody widzimy również sposób jej uruchamiania: HttpTrigger. W tym miejscu możemy skonfigurować parametry tego wyzwalacza. Przejdźmy zatem do uruchomienia naszej funkcji. Zaznaczę, że pierwsze włączenie może być trochę dłuższe, gdyż mogą zostać doinstalowane pewne zależności. Jeśli wszystko poszło zgodnie z planem, to na konsoli wypisał nam się adres, pod którym możemy uruchomić funkcję.

Warto w tym momencie ustawić breakpoint’a (skrót F9 w Visual Studio) na samym początku naszej metody. Skopiujmy teraz powyższy adres oraz wklejmy go w przeglądarkę (uruchomi to metodę GET). Dopiszmy na końcu również parametr określający nazwę użytkownika (np. http://localhost:7071/api/Function1?name=Cesarz).

Jak widać na powyższym obrazku, wywołanie tego adresu w przeglądarce uruchomiło naszą funkcję. Brawo! Waśnie napisaliśmy, oraz uruchomiliśmy naszą pierwszą Azure Functions! Jest to bardzo proste i szybkie. Przejrzyjmy jeszcze inne opcje wyzwalania. Jednak przed tym chcę wspomnieć, że jest jeszcze wiele innych trigger’ów! Możliwości są ogromne, więc warto poeksperymentować oraz doczytać na ten temat.

  • QueueTrigger – uruchamiany wiadomością na kolejce
  • BlobTrigger – uruchamiany po utworzeniu/modyfikacji/usunięciu danych w magazynie (Azure Storage)
  • TimeTrigger – uruchamiany cyklicznie
  • EventHubTrigger – uruchamiany przy odpowiednim zdarzeniu w EventHub’ie
  • SignalRTrigger – uruchamiany przy komunikatach wysyłanych z usługi Azure Signal Service

Podsumowanie

Przetwarzanie bezserwerowe powstało w celu rozwiązania konkretnych problemów. Zdecydowanie nie jest lekiem na wszystko, jednak doskonale nadaje się do odpowiednich zagadnień. Powyższy wpis przedstawia wyłącznie podstawowe zagadnienia związane z Azure Functions. Dajcie znać (najlepiej pisząc komentarz, bądź wysyłając maila na admin@cesarstwo-dev.pl) czy chcecie przeczytać kolejny wpis na ten temat! Na pewno chciałbym poruszyć w nim zagadnienia związane ze wdrożeniem na środowisko chmurowe (dziś skupiliśmy się na testowaniu lokalnym), implementacją wstrzykiwania zależności, jak i wspaniałemu rozszerzeniu, jakim jest Durable Functions.

Czas już kończyć! Mam nadzieję, że podobał Wam się ten wpis oraz mogliście z niego wynieść co najmniej jedną pożyteczną informację. Jeśli jest to Wasz pierwszy wpis na tym blogu, to może chcecie przeczytać na temat architektury heksagonalnej w C#? Wpis ten przedstawia tą architekturą na prostym przykładzie. Niezależnie, bardzo Wam dziękuję za obecność tutaj, jak i za wytrwanie do końca tego artykułu. Do usłyszenia w kolejnym wpisie w Cesarstwie-Dev!

PS. Zdecydowałem zamieszczać książkową aktualizację na koniec każdego wpisu!

W kolejce:

  • Netflix – to się nigdy nie uda
  • Mikroserwisy w akcji

W trakcie:

  • Software Craftsmanship
  • Kubernetes – Wzorce projektowe
Kategorie: Techniczne

2 Komentarze

Gość · 2020-10-05 o 15:38

„Wstrzykiwanie zależności i Durable Functions” +1. Niewiele fajnych materiałów na ten temat, więc głosuję za :).

dotnetomaniak.pl · 2020-10-04 o 23:16

Podstawy Azure Functions

Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl

Dodaj komentarz

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