Cześć wszystkim, poprzedni tydzień był bardzo mało “newsopędny”. W związku z tym postanowiłem spełnić swoje małe marzenie i przygotować roboczą wersję czegoś, co w przyszłości chciałbym zmienić na State of Java Reactivity 2021 – przegląd tego, jak bardzo “reaktywne” są poszczególne Javowe rozwiązania pod koniec pierwszego kwartału roku 2021.

Całość to bardziej “listicle” niż dogłębna analiza – w sam raz do poczytania do kawki (albo do paru kawek – całość jest jednak dość długa). Moim celem było jak najszersze przedstawienie tematu, z dużą ilością materiałów dodatkowych, które każdy będzie mógł przeczytać w wypadku, gdy będzie chciał sobie poszerzyć wiedzę o poszczególnych zagadnieniach.

Co jest out-of-scope:

  • Korutyny oraz Project Loom. Tekst i tak wyszedł bardzo przekrojowy, dołożenie tej dwójki tylko skomplikowałoby i tak już szeroki obraz całości.
  • Inne języki niż Java. Postanowiłem się w tym przeglądzie ograniczyć wyłącznie do Javy i jej ekosystemu. Większość z zaprezentowanych rozwiązań z powodzeniem użyć można w innych językach JVMowych – mają tam swoje odpowiedniki.

A, i zakładam, że wiecie, czym programowanie reaktywne jest, a fundacje ReactiveX macie w swoich zakładkach – nie miejsce tu i czas, żeby tłumaczyć, z czym ten cały trend się je. Jeśli potrzebujecie odświeżyć sobie te zagadnienia, to moim ulubionym wprowadzeniem w temat jest chyba “Notes on Reactive Programming” z blogu spring.io.

No to lecim 🚀

Podstawy

Pierwszą rzeczą, od której sprawdzenia zacząłem, to zaglądnięcie do korzeni tematu i sprawdzenie, na ile aktualna jest moja wiedza na temat Reactive Manifesto. Okazało się, że akurat ten trzyma się w swojej drugiej, wydanej w 2014 roku wersji.

Oczywiście, nie oznacza to, że Reactive Foundation spoczęło na laurach – końcówką zeszłego roku ukazało się niejako “rozszerzenie” całego Manifestu – publikacja Reactive Principles. Jest to arcyciekawy dokument, m.in. żeniący systemy reaktywnę z ich niejako dzisiejszym rozwinięciem - Cloud Native. Rozszerza także oryginalne cztery cechy o zestaw ośmiu pryncypiów, których celem jest doprecyzowanie, jak takie systemy w praktyce pisać.

Oprócz pisania przewodników i manifestów, Reactive Foundation opiekuje się też różnorakimi projektami. O dwóch z nich (RSocket i R2DB) wspomnę w dalszej części, na razie zaś spójrzmy, jak aktualnie prezentuje się trzeci – Reactive Streams.

Reactive Streams to inicjatywa, która w okolicach premiery JDK 9 była jednym z najbardziej gorących tematów społeczności. Jej cel był szczytny – miała ustandaryzować API projektów korzystających z konceptów reaktywnych, umożliwiając ich wzajemną kompatybilność. Według mnie sukces jest połowiczny. O ile rzeczywiście opublikowane przez Reactive Streams interfejsy stały się złotym standardem implementowanym przez wszystkie biblioteki, o tyle już kompatybilności można mieć trochę do zarzucenia – do zapewnienia kompatybilności RxJavy i Reactora niezbędny jest dodatkowy adapter.

Najnowsza wersja standardu to edycja 1.0.3, wydana pod koniec roku 2019. W stosunku do poprzedniczek zawiera jedynie poprawki błędów.

No, to mając za sobą podstawy podstaw, pora przejść do “mięska”. Nasz następny przystanek to biblioteki.

Biblioteki

RxJava

To właśnie RxJava od Netfliksa przetarła szlaki innym projektom związanym z reaktywnym programowaniem. Jej oryginalna wersja powstała jeszcze w czasach, gdy nikt nawet nie planował Reaktywnych Strumieni, i dopiero wydanie 2.0 wprowadziło zgodność z tym standardem. Sprawiło to, że migracja między wersja 1.0 i 2.0 była straszliwie czasochłonna i kosztowała masę pracy (been there, done that, still hurts 😵).

Aktualna wersja biblioteki to 3.0.11, która ukazała się w zeszły piątek (i była de facto jednym z motywatorów dzisiejszego wydania naszego newslettera), reprezentując już trzeciego “majora” biblioteki. Jest to wydanie niezwykle istotne, ponieważ wraz z nim swój żywot zakończyła gałąź 2.x, przez co rekomendowane jest podbicie się do nowego wydania. Mimo bardzo długiej listy zmian między 2.x a 3.x, migracja jest tym razem dużo przyjemniejsza. Główna zmiana jaką przynosi 3.x to podbicie minimalnej wersji Javy do 8, przez co twórcy mogli zapewnić kompatybilność np. ze Stream API czy javowymi Optionalami.

Jeżeli chcecie być bardzo na bieżąco z rozwojem projektu, możecie przyłączyć się do oficjalnej grupy mailingowej, aczkolwiek nie jest ona zbyt żywa.

🐦 Oficjalne konto Twitterowe

Reactor

Jeżeli chodzi o pretendentów do “tronu”, to głównym konkurentem dla rozwiązania Netfliksa jest Reactor. Rozwijana przez Pivotala biblioteka, której niewątpliwą zaletą jest to, że stanowi reaktywny trzon Springa, od lat stanowiącego “domyślne” rozwiązanie, jeśli chodzi o pisanie aplikacji webowych w Javie.
Ze względu na tą swoistą symbiozę, Reactor obrósł w masę connectorów do wszelkich popularnych technologii. Posiada gotowe wsparcie min. Netty’ego, Kafki czy RabbitMQ. Projekty te są również bardzo dynamicznie rozwijane – np. ostatni commit do reactor-kafka został zmergowany w piątek.

Z informacji praktycznych – model działania Reactora jest nieco prostszy niż ten RxJavy. Efektem tego jest jego mniejsza elastyczność, ale z drugiej strony próg wejścia jest znacząco mniejszy.

Jeżeli potrzebujesz zdecydować między użyciem RxJavy, a Reactorem, pomocny może być artykuł Tomka Nurkiewicza. Co prawda pochodzi z 2019 roku, w związku z czym niektóre z jego tez już się zestarzały (jak wspomniałem w kontekście RxJavy, ta też porzuciła już wsparcie dla Javy 6, co nie stanowi już dziś problemu nawet przy programowaniu na Androida). Sam tekst pozostaje jednak jedną z lepszych analiz porównawczych w internecie. @tnurkiewicz – może czas na aktualizacje?

🐦 Oficjalne konto Twitterowe

RSocket.io

No i dochodzimy do pierwszego z dwóch obiecanych projektów wspieranych przez Reactive Foundation. W odróżnieniu od poprzedników, mimo iż RSockets również implementuje standard Reactive Streams, nie jest to “generyczna” biblioteka do obsługi reaktywności w aplikacji, a gotowa implementacja protokołu binarnego, będącego alternatywą dla HTTP czy gRPC. RSockets zresztą w “bebechach” używa Reactora (co jeszcze bardziej pokazuje elastyczność rozwiązania Pivotala). RSocket posiada bardzo dobrze napisaną dokumentacje – szczególnie ciekawe są fragmenty, gdzie jego twórcy wyłuszczają powody jego stworzenia (bardzo polecam).

Jeżeli chcecie dowiedzieć się jak RSocket prezentuje się w kontraście do głównego konkurenta, gRPC, bardzo polecam ten artykuł. RSocket jest też jednym z ulubionych tematów Josha Longa (Developer Advocate Springa), i to też jego teksty mogę najbardziej polecić jako wprowadzenie do tematu. RSocket posiada zresztą oficjalny initializer do Springa (spring-boot-starter-rsocket), co sprawia że integracja tych dwóch projektów ze sobą jest trywialna.

🐦 Oficjalne konto Twitterowe

Jakarta EE API - JAX-RS & Mutiny

A na koniec sekcji bibliotek zostawiłem sobie moje Guilty Pleasure – Jakarte EE. Otóż jedna z jej głównych specyfikacji, JAX-RS również wraz z wersją 2.1 (czyli już w 2017 roku), doczekała się wsparcia dla Reactive Streams. Najpopularniejszą (i po prawdzie jedną z niewielu) implementacji standardu jest RESTEasy, który to w internalach opiera się na RxJavie.

JAX-RS podobne jest w swoim działaniu do WebFluxa (którym to zajmiemy się za moment), ukrywając tak naprawdę całą reaktywność poprzez swoje deklaratywne API, przez co na pierwszy rzut oka trudno jest zauważyć, że korzysta się z tego paradygmatu. Na pewno jest to bardzo ciekawa opcja dla osób piszących w nowoczesnej Javie/Jakarcie EE – minimalna edycja standardu to wersja 8.

Dodatkowo, mało kto wie o istnieniu Smallrye Mutiny. Jest to świeżutki, zapoczątkowany pod koniec 2019 roku projekt, którego celem jest stworzenie wolnej, ustandaryzowanej implementacji Reactive Streams na potrzeby Jakarty EE, niezależnej od Netfliksa czy Pivotala. Mimo młodego wieku, projekt ten osiąga pewne sukcesy, stanowiąc aktualnie budulec zarówno Vert.x, jak i Quarkusa.

Akka-Streams

Na koniec mała niespodzianka. Podejrzewam, że większości z Was Akka-Streams kojarzy się ze Scalą. Jest to opinia, którą Lightbend (twórcy Akki), bardzo starają się zmienić. Podobnie zresztą jak to, że w 2021 mało kto jeszcze kojarzy model aktorowy z programowaniem reaktywnym, mimo że idealnie wpisuje się on w oryginalne Reactive Manifesto. Faktem pozostaje jednak że Akka-Streams, która wraz z RxJavą trendowi reaktywnemu przodowała w jego początkowych latach, nigdy mocno nie przebiła się do świadomości. Przynajmniej firm, które już wcześniej nie były zaangażowane w królową JVMowego programowania funkcyjnego - mimo faktu, że od bardzo dawna Akka posiada swoje Javowe API.

Przyznam, że zrobiłem mały przegląd i bardzo trudno znaleźć w internecie jakieś duże przypadki użycia tego projektu poza partnerami Lightbend. Ze względu na to ciężko też z odpowiednią dozą uczciwości porównać go z innymi dostępnymi na rynku rozwiązaniami. Zdecydowanie nie mogę nazwać uczciwym porównania z 2015 roku, które od tamtej pory krąży po sieci i robi rozwiązaniu bardzo złą opinie.

Jeżeli miałbym wskazać jakąś dużą zaletę Akki, jest nią z pewnością Alpakka - zbiór wszelkiej maści connectorów do takich zewnętrznych systemów jak Kafka, Azure Queues, czy RabbitMQ. Pod tym względem, rozwiązanie Lightbendu zbliżone jest nieco do tego, co oferuje Springowy Reactor.

Cykl życia Akki-Stream związany jest bardzo z oryginalną Akką i nowe edycje wersji opartej na reaktywnych strumieniach dostajemy w komplecie z “głównym” wydaniem. Najnowsza “duża” wersja, Akka 2.6, opublikowana została w grudniu 2019 i przyniosła duży refactor API, między innymi tego głównego, aktorowego.

Frameworki

Teraz, mając najciekawsze biblioteki za sobą, czas wejść poziom wyżej i przejść do frameworków.

Vert.x

Pozwolę sobie zacząć od frameworku bardzo bliskiego memu sercu, bo to właśnie jego trzecie wydanie nauczyło mnie najwięcej w temacie programowania reaktywnego. Vert.x wybił się na popularności Node.js w czasach, gdy wszystkim wydawało się, że Event-Loop będzie “złotym standardem”, jeśli chodzi o programowanie asynchroniczne. Jego gwiazda od tamtych czasów nieco już przygasła. Mimo wszystko, ze względu na bardzo “niskopoziomowy” sposób pisania, dalej pozostaje on bardzo ciekawym rozwiązaniem dla tych, którzy wymagają od swoich aplikacji maksymalnego poziomu wydajności. Rozwiązanie oparte jest na nieblokującym serwerze Netty, a dzięki swojemu unikalnemu systemowi “wierzchołków” cechuje się potężną elastycznością.

W grudniu zeszłego roku ukazała się czwarta edycja tego frameworku. Jeśli chodzi o zmiany w kontekście reaktywności jako takiej, przynosi on klientów zarówno do Redisa, jak i baz SQLowych (np. Postgresowa implementacja opiera się na publikowanym przez tą bazę interfejsie NOTIFY/LISTEN).

Reaktywne rozwiązania Vert.x opierają się na RxJavie. Co ciekawe, mimo tego, że jak wcześniej pisałem, wersja 2.x nie jest już oficjalnie wspierana, ciągle to właśnie ona stanowi budulec dla rozwiązań w ramach Vert.x. Jeżeli RxJava nie zostanie dostarczona, Vert.x używał będzie wspomnianej biblioteki Mutiny.

🐦 Oficjalne konto Twitterowe

Spring Framework / Boot

Z rozwiązania bardzo niszowego pora przejść na takie, które kojarzyć będzie zapewne każdy programista Javy. Spring od lat króluje wśród Javowych frameworków i choć powoli zaczyna mu się rodzić realna konkurencja, to jednak nic nie sugeruje tego, aby ktokolwiek miał zdetronizować go w najbliższym czasie z pierwszego miejsca.

Duża w tym zasługa tego, że choć był krótki okres, gdy Spring złapał nieco zadyszki, to jednak wraz z mikroserwisową rewolucją, trafił on w dziesiątkę ze swoją wersją Spring Boot. To właśnie jego WebFlux wprowadził też reaktywność “pod strzechy”.  Pivotal w porę zauważył popularność tego paradygmatu i uczynił z niego “first-citizena”. Spring posiada też wsparcie dla reaktywnego połączenia z bazą danych za pomocą projektu r2dbc, o którym wspomnę pod koniec artykułu.

Aktualnie wszyscy czekamy na wydanie Spring Boota w wersji 2.5 (prywatnie już przebieram nóżkami) – obecnie pojawił się jego pierwszy Milestone. Aczkolwiek, jako że nie przynosi ono żadnych większych zmian w kontekście reaktywności, pozostaje de-facto poza zakresem tematycznym tego artykułu.

A, i nie muszę chyba dodawać, że całość “zasilana” jest Reactorem, prawda 😉?

🐦 Oficjalne konto Twitterowe

Micronaut

Kiedy wspominałem o tym, że Spring posiada godnych rywali, w głowie kołatał mi właśnie m.in. Micronaut. Rozwiązanie od twórców świętej pamięci Grailsów, stara się grać kartą bardzo podobnej, aczkolwiek “lżejszej” alternatywy dla Springa. Jako jeden z pierwszych frameworków zwietrzył on też okazję, jaką z pewnością jest GraalVM. W ten sposób umościł się na pozycji “tego drugiego”, mimo że czuje raczej za plecami oddech Quarkusa.

Jeżeli chodzi o swoje podejście do reaktywności, Micronaut jest niezwykle pojemny, a równocześnie mało wybredny – można go bowiem używać zarówno z RxJavą, jak i Reactorem. W kwestii połączenia do baz danych, używa on wspomnianych już klientów napisanych na potrzeby Vert.x’a.

Quarkus

Quarkus, kolejny z pretendentów podgryzających Springa, jest reprezentantem ciągle dość raczkującego nurtu nowoczesnych frameworków Jakarty EE. W tym gronie to właśnie on zdobył największą miłość społeczności, a jego kolejne wydania obserwowane są z dużym zainteresowaniem. Rozwiązanie pozycjonuje się jako bardzo GraalVMowe i bardzo Cloud Native.

Chcąc uchodzić za nowoczesny JVMowy framework Javowy, bardzo ciężko jest przejść obojętnie obok całego reaktywnego trendu. Trzeba jednak przyznać, że Quarkus załapał się na ten pociąg stosunkowo późno. Dopiero w grudniu 2020 świat obeszła wiadomość, że framework od Red Hata (ciekawym jest to, że swego czasu Red Hat zaangażowany był też w rozwój Vert.xa) dostanie pełne wsparcie dla wspomnianego już JAX-RS 2.1, a co za tym idzie również reaktywnych kontrolerów. Jest to jedyne z opisywanych przeze mnie dzisiaj rozwiązań implementujących ten standard. Dodatkowo, wszelkie inne reaktywne fragmenty Quarkusa używają Mutiny – jestem bardzo ciekaw, w jakim kierunku pójdzie ta biblioteka, na pewno będę obserwować jej rozwój.

🐦 Oficjalne konto Twitterowe

Bazy Danych

Na koniec zostawiłem sobie bazy danych. Przez długi czas, to właśnie one były piętą achillesową wszelkich reaktywnych systemów pisanych w Javie. Społeczność długie lata czekała, aż Oracle w końcu udostępni reaktywną wersje JDBC, zwaną ADBA (Asynchronous Database Access). Projekt przeciągał się, by ostatecznie pod koniec roku 2019 zostać zupełnie wygaszony. Bardzo interesujące są powody – otóż Oracle stwierdziło, że przyszłość Javy tkwi nie w asynchronicznym kodzie, a Projekcie Loom, który ma przynieść do Javy zupełnie nowe API wątków, zwanych włóknami.

Nie ma jednak tego złego co by na dobre nie wyszło. Jako że Spring potrzebował wesprzeć swojego WebFluxa jakimś reaktywnym sterownikiem, stworzył on projekt R2DBC – reaktywną implementację sterowników do popularnych baz danych. Aktualnie jest on dostępny w wydaniu stabilnym i jest rekomendowany do użycia na produkcji. Całość została również przekazana Reactive Foundation – jest to drugi obiecany na samym początku projekt tej fundacji. Równocześnie, jak już wspomniałem, cały czas rozwijani są klienci SQL tworzony na potrzeby Vert.xa.

Opisując reaktywne sterowniki do baz danych, nie mogę również nie wspomnieć, o tym stworzonym na potrzeby MongoDB, jako że jest on dostarczony przez twórców bazy i w pełni kompatybilny ze standardem Reactive Streams.

Na sam koniec mam dla was bombę z wczoraj. Otóż ukazała się stabilna wersja reaktywnego Hibernate. Twórcy chwalą się jego wysoką kompatybilnością z Quarkusem i Vert.xem (używa pod spodem klientów SQL tego ostatniego), a także bardzo wygodną współpracą z RESTEasy Reactive.


I tym właśnie newsem zakończyć pragnę nasze “krótkie” podsumowanie, które chyba zasługuje na nazwę “State of Java Reactivity 2021”. Podejrzewam, że jest trochę niekompletny (wystarczy spojrzeć na przegląd reaktywnych rozwiązań, który można znaleźć na stronie Reactive Foundation) - może chcielibyście wersję rozszerzoną tekstu? Jeżeli się Wam podobało, serdecznie zapraszam do subskrypcji, a także do używania naszego Keep Upa. Dzięki niemu unikniecie w przyszłości takiego potężnego info-dumpu – na bieżąco będziemy mogli informować Was o wszystkich ważnych nowinkach w Javie (i nie tylko).