Załóżmy, że mamy taki pomysł, żeby skorzystać z informacji, które udostępnia Wrocław w ramach inicjatywy Open Data. A dokładniej, z informacji dotyczących opadów w mieście.
Gdybyśmy chcieli zrobić to tak po prostu…
Dane, o których piszę na wstępie, udostępnione są na stronie https://www.wroclaw.pl/open-data/dataset?tags=Deszcz Nic więc nie stoi na przeszkodzie, żeby tam wejść i pobrać odpowiedni plik w formacie CSV.
Sprawa się jednak komplikuje…
Do jednorazowego pobrania pliku, przeglądarka jest narzędziem w zupełności wystarczającym. Co, jednak jeżeli postanowimy pobierać te dane cyklicznie, dajmy na to codziennie. I umieszczać je w chmurze. A na dodatek, zrobić to wszystko w Kotlinie?
Serverless, Kotlin i Google Cloud
Usługą Google Cloud, która najlepiej nadaje się do uruchamiania aplikacji "od czasu do czasu" (w naszym przypadku raz dziennie) jest Google Cloud Functions. Jest to rozwiązanie serverless, pozwalające na wykonywanie kodu bez konieczności zarządzania infrastrukturą. Skoro już pracujemy całkowicie w chmurze, to pobrane dane będziemy odkładali w kubełku Google Cloud Storage
No to do dzieła
Serwis działający w ramach usługi Google Cloud Functions może być uruchamiany na kilka sposobów:
-
żądanie http
-
zmiana w Google Cloud Storage
-
sygnał z kolejki pub/sub
Należy tutaj dodać, że zajmujemy się usługą w wersji V1. Rewizja V2 to nieco inna historia.
Jak widać, na powyższej liście nie ma "wyzwalacza czasowego". Można go jednak w prosty sposób zasymulować.
Google Cloud Pub/Sub
Konfigurowanie naszej aplikacji zacznijmy od utworzenia nowego topica:
$ gcloud pubsub topics create rainfall-topic
Listę topiców utworzonych w naszym projekcie możemy sprawdzić poleceniem
$ gcloud pubsub topics list
Po utworzeniu topica możemy przejść do konfiguracji kolejnego elementu naszego systemu.
Google Cloud Scheduler
Google Cloud Scheduler to usługa planowania zadań w chmurze Google Cloud. Umożliwia ona uruchamianie zadań cyklicznie lub jednorazowo w określonych terminach.
Dla naszych potrzeb utworzymy zadanie (job), które będzie raz dziennie o północy, umieszczało w topicu wiadomość w formacie JSON.
W tym celu wykonujemy polecenie:
$ gcloud scheduler jobs create pubsub rainfall-fetch-job --schedule="0 0 * * *" --topic=rainfall-topic --message-body="{\"message\": \"fetch\"}" --time-zone="Europe/Warsaw"
Tworzy ono nowe zadanie o nazwie rainfall-fetch-job, które będzie raz dziennie (0 0 * * *), umieszczało w topicu o nazwie rainfall-topic, wiadomość {"message": "fetch"}. Wszystko to będzie się działo w strefie czasowej Europe/Warsaw.
Po skonfigurowaniu topica oraz utworzeniu zadania możemy zająć się tworzeniem samej funkcji.
Google Cloud Functions
Jak wspominałem na początku, jedną z metod uruchamiania funkcji, jest pojawienie się nowej wiadomości w topicu pub/sub. Do napisania takiej funkcji, najłatwiej wykorzystać Google Cloud Functions Framework.
Składa się ona z obiektu opisującego przesyłany komunikat w formacie JSON. Dla naszego przykładu będzie to:
data class Ping(val data: String)
Oraz samej funkcji, która w najprostszej formie wyglądać może następująco:
class FetchFunction: BackgroundFunction<Ping> { private val logger = LoggerFactory.getLogger(this.javaClass) override fun accept(payload: Ping?, context: Context?) { if (Base64.getDecoder().decode(payload?.data).decodeToString() == PING_FETCH_MESSAGE) { logger.info("Hello World!") } } }
Pełen projekt, wykorzystujący serwisy opisane powyżej znajduje się tutaj.
Najwygodniej, funkcje napisane przy wykorzystaniu JDK instaluje się w chmurze Google, w postaci „jednego jara” (fatjar). Dla naszego przykładowego projektu robimy to poleceniem:
$ ./gradlew shadow
Tak zbudowany plik wdrażamy następującym poleceniem:
$ gcloud functions deploy fetch_function --source=build/libs/ --entry-point com.zamolski.rainfallmeasurer.FetchFunction --trigger-topic rainfall-topic --env-vars-file env-vars.yaml --runtime java17 --region europe-central2
Polecenie to instaluje w chmurze funkcję o nazwie "fetch_function".
Znaczenie poszczególnych parametrów poniżej:
-
--source - miejsce, z którego instalujemy funkcję. Należy pamiętać, aby we wskazanym katalogu był tylko jeden plik .jar
-
--entry-point - pełna nazwa pakietowa wdrażanej funkcji
-
--trigger-topic - topic, na którym ma nasłuchiwać funkcja
-
--env-vars-file - ścieżka do pliku, w którym znajdują się zmienne systemowe niezbędne do działania funkcji.
-
--runtime - środowisko uruchomieniowe funkcji-w tym przypadku "JDK 17"
-
--region - wiadomo, najbliższa bądź najwygodniejsza lokalizacja serwerowni GCP
A w następnym odcinku zastanowimy się, co dalej można zrobić z danymi zapisanymi w kubełku.