Dzisiejszy wpis poświęcony będzie usłudze Azure Bot Service, co do której miałem całkiem spore oczekiwania. Czy usługa im sprostała? Zapraszam do lektury :)
The good
Zaczynamy od rzeczy pozytywnych: Azure Bot Service stawia się łatwo i przyjemnie, kilka kliknięć i po chwili mamy gotowe środowisko z przykładowym botem. Do wyboru mamy język C# lub JavaScript (node.js) oraz kilka szablonów botów o różnej funkcjonalności:
Po wdrożeniu całego środowiska lądujemy w przeglądarkowym edytorze kodu, w którym od razu możemy przystąpić do tworzenia naszego bota:
W ramach tej usługi Azure wdraża nam automatycznie wszystko co jest potrzebne do odpalenia bota, a więc w szczególności:
- Usługi AppService wraz z Azure Functions.
- Konfiguruje rejestrację bota na portalu botframework.com.
- Konfiguruje połączenie między Azure Functions oraz Bot Connectorem.
- Daje łatwą możliwość skonfigurowania automatycznego deploymentu z repozytoriów gita (i nie tylko).
- Konfiguruje kanał do komunikacji przez Skype.
Fajne jest to, że cały model oparty jest o plan AppService typu Consumption, a więc płacimy tylko za faktyczne wywołania Azure Functions co w praktyce oznacza niemal zerowe koszty utrzymania takowego bota na początku, a w przyszłości płatność tylko za faktyczne użycie bota.
Uproszczony interfejs Azure Bot Service daje szybki dostęp do podstawowych funkcji i ustawień, co świetnie sprawdza się w podstawowych zastosowaniach.
The bad
I właśnie wspomniane w poprzedniej sekcji podstawowe zastosowania są słabą stroną tej usługi, bo gdy tylko chcemy osiągnąć coś więcej, Azure Bot Service z miłego przyjaciela staje się naszym przeciwnikiem, z którym musimy powalczyć.
Pierwszy problem, z jakim się spotkałem dotyczy dodania nowych Azure Functions, poza standardową do obsługi wiadomości dla bota. Otóż możemy próbować to robić, ale w praktyce jest to dość ciężkie do zrealizowania, gdyż nie mamy dostępu do blade’a azurowego do obsługi Azure Functions…
Edytor przeglądarkowy też szybko staje się dużym ograniczeniem, gdyż nie możemy… utworzyć w nim nowych plików! Nie ma więc mowy o dodaniu nowych funkcji bez wrzucania kodu z zewnątrz. To akurat nie boli, bo raczej nie spodziewałbym się, że ktokolwiek tworząc konkretny projekt będzie chciał go tworzyć w ramach edytora w przeglądarce.
Boli natomiast fakt, że gdy tylko pobierzemy na dysk przykładowy projekt to okazuje się, że… nie działa on w Visual Studio 2015. Tak, zaktualizowałem Visual Studio. Tak, zainstalowałem Azure SDK. Tak, zainstalowałem wszystkie wymagane przez Azure Functions narzędzia. Nie, nadal nie działa.
Problem powyższy rozwiązałem tworząc od zera pliki solution i projektu, całość szablonu typu Proactive Bot dla C# znajdziecie na moim GitHubie.
Ok, mamy już coś co działa w VS2015, wrzucamy więc nowe funkcje np. typu timer (wykonywane co zadany interwał, dość częsty przypadek) do Azure Bot Service. I co? Cóż… nie działa. Po zapoznaniu się z dokumentacją Azure App Service wychodzi na to, że aby funkcje typu timer działały musimy w ustawieniach zaznaczyć opcję Always On. Ok… wchodzimy więc do blade’a Application settings, aby zgodnie z dokumentacją włączyć funkcję Always On. Wita nas mało optymistycznie nastrajający komunikat:
Nie poddajemy się… jak tu zmienić plan na Basic? Okazuje się, że nie ma takiej opcji (Change App Service Plan nie jest dostępny w menu) :P a co za tym idzie – mówimy funkcjom typu timer „do widzenia”.
Przy tworzeniu Azure Bot Service nie mamy też niestety opcji wyboru jaki plan chcielibyśmy realizować, więc opcja z usunięciem usługi i ponownym utworzeniem we właściwym planie nic nam nie daje.
A może dodamy funkcję z wywoływaną przez HTTP i np. podepniemy do jakiegoś narzędzia monitorującego, które wywoła nam ten endpoint co jakiś czas (takie lekkie obejście) ;)
Sprawdziłem. Dodałem. Problem jest taki, że nie mamy dostępu do blade’a z ustawieniami funkcji więc nie odczytamy kodu autoryzującego wywołanie funkcji…
Ostatecznie stanęło na tym, że – co jest specyficzne dla projektu typu reactive bot – tworzona jest też druga usługa Azure Functions, w niej mamy dostęp do blade’a z ustawieniami kodów autoryzacyjnych funkcji – tam udało się wszystko ustawić, natomiast tak w innych typach projektów musimy poradzić sobie w inny sposób.
Po fakcie dotarłem też do dokumentacji, w której powiedziane jest jak ręcznie ustawić kody poprzez edycję plików (poza wwwroot, a więc nie są uwzględniane w procesie deploymentu) przy użyciu narzędzia Kudu. To być może by zadziałało w ramach podstawowych Azure Functions w ramach Bot Service, ale… nie miałem już siły żeby to testować…
Reasumując – o ile mieścimy się w absolutnie podstawowych zastosowaniach i nie chcemy nic wykraczającego poza prostego bota przewidzianego przez projektantów to wszystko jest ok (no prawie, patrz sekcja „The ugly”).
Gdy potrzebujemy czegoś więcej to Azure Bot Service staje się ograniczeniem i prawdopodobnie szybciej osiągnęlibyśmy swój cel ręcznie komponując całe środowisko w Azure wg naszych wymagań.
The ugly
Dochodzimy teraz do sedna problemu z Azure Bot Service, choć właściwie problem jest związany z podstawami, na których zbudowana jest Azure Bot Service, w szczególności mam na myśli Azure Functions.
Co jest z nimi nie tak? Teoretycznie wszystko jest fajnie, tworzymy w VS2015 nową funkcję i piszemy jej zawartość w… pliku .csx.
Ok, rozumiem, ideą funkcji było szybkie wrzucanie kawałków kodu, stąd wybór C# Scriptu i rezygnacja z całego boilerplate’u związanego z pełnym projektem w Visual Studio. Tyle, że ja tutaj chcę napisać porządny i solidny kod, który wykracza poza proste zastosowanie i będzie miał wiele komponentów, klas itd. Pisania całości w plikach .csx po prostu sobie nie wyobrażam.
Sięgam więc do dokumentacji. Szukam, szukam, jest! Precompiled functions – czyli mechanizm pozwalający pozbyć się .csx-ów i dający mi możliwość napisania klasycznego class library i użycia kodu z tego library jako Azure Function.
Kilka testowych linijek, deployment, działa! Następnie zachęcony poprzednim sukcesem napisałem w ten sposób fragment mojego SmogBota odpowiedzialny za aktualizację danych o zanieczyszczeniu powietrza i wrzucanie tych danych do bazy. Działa tak jak chciałem :)
To jedziemy dalej. Zaczynam przenosić utworzone dotychczas fragmenty bota do class library, podpinam to library do funkcji. Rozwiązanie sprawia wrażenie, że działa… z akcentem na „sprawia wrażenie” :P
I tutaj dochodzimy do sedna problemu z Azure Bot Service: usługa ta kompletnie nie nadaje się tworzenia botów w class library, gdyż BotBuilder nie jest w stanie poprawnie funkcjonować w środowisku Azure Functions!
Powtórzę raz jeszcze: zapomnijcie o tym, że w łatwy sposób utworzycie cały projekt w class library ze wszystkimi perkami z tym związanymi (IntelliSense, R#, normalne zarządzanie zależnościami, ładne testy etc). BotBuilder, będący dość dużą częścią BotFrameworka, na którym MS opiera tworzenie botów nie zadziała. Nie wiem czy ktoś to testował w MS, czy też wyszli z założenia, że nikt nie będzie chciał pisać normalnych class library, skoro mają .csx, a może po prostu jestem jedynym człowiekiem na ziemi, który wpadł na taki pomysł :P
Przeskoczmy teraz o kilkanaście godzin debugowania, jakie poświęciłem na zdiagnozowanie problemu, aby dowiedzieć się dlaczego nie działa:
Problem polega na tym jak napisany jest system dialogów w MS Bot Framework. W skrócie: każdy dialog musi być oznaczony jako Serializable, cały stack dialogów odpowiedzialny za określenie w którym „miejscu” konwersacji jest użytkownik bota jest natomiast serializowany binarnie i zapisywany w usłudze persystencji stanu.
I to właśnie serializacja binarna jest tutaj problemem – trafiamy bowiem na klasyczny problem związany z deserializacją klas z innego assembly, natomiast klasyczne rozwiązanie tego problemu poprzez implementację bindera szukającego assembly dla danego typu niestety się nie sprawdza i szczerze powiedziawszy nie wiem jak to rozwiązać i niekoniecznie mam ochotę poświęcać kolejnych kilkunastu godzin na kolejne próby… zgłosiłem więc issue do projektu Bot Buildera, być może autorzy będą mieli lepszy pomysł na rozwiązanie tego problemu.
Podsumowanie
Na chwilę obecną powyższe zepsuło mi plan, który chciałem realizować w oparciu o Azure Bot Service. Ta usługa po prostu nie sprostała wymaganiom stawianym dla mojego bota, nie pozostaje więc nic innego jak… ręcznie utworzyć całe środowisko w oparciu o standardowe Azure Web Apps, które niestety wiążą się z dodatkowymi kosztami, natomiast nie stawiają praktycznie żadnych ograniczeń.
Azure Bot Service można natomiast z powodzeniem wykorzystać jako szybki sposób do utworzenia całej infrastruktury – tj. tworzymy standardowo bota w Azure Bot Service (wraz ze wszystkimi dodatkowymi zasobami) a następnie – usuwamy tylko zasób Bot Service! Z moich testów wynika, że wszystko inne (w szczególności konfiguracja bota na botframework.com – a co za tym idzie – kanałów dostępu) zostaje nietknięta, a więc wystarczy zmienić endpoint do komunikacji i gotowe :)
Ja jednak trochę się zraziłem do Azure Bot Service, więc poświęcę te kilkanaście minut i samodzielnie sobie wszystko wyklikam od podstaw.
Dodaj komentarz