Kategorie
java

Quarkus Reactive Routes

Gdy chcemy mieć większą kontrolę nad obsługą żądań w naszej aplikacji, możemy skorzystać z Quarkus Reactive Routes. Większa kontrola to również większa odpowiedzialność. A co za tym idzie, musimy dużo lepiej rozumieć mechanizm, który daje nam tę możliwość.

Quarkus Reactive Routes question

Kod do niniejszego artykułu znajduje się tutaj:

https://github.com/bchmielewski/quarkus-reactive-routes

Dlaczego Reactive Routes?

W poprzednim artykule zajrzeliśmy do środka budowy Quarkusa. Wspomnieliśmy przy tej okazji, że Reactive Routes jest alternatywnym rozwiązaniem do budowania API opartego o HTTP.

Architektura Quarkus - Reactive Routes
Architektura Quarkus – Reactive Routes

Na podstawie powyższego diagramu widzimy, że w przypadku rozszerzenia Reactive Routes obsługa żądań nie będzie odbywać się domyślnie w ramach tradycyjnej, oddzielnej puli wątków (tzw. worker threads).

Oznacza to, że już na starcie będziemy mieli możliwość samodzielnej decyzji, w jaki sposób obsłużymy nasze żądanie. Co za tym idzie, to od nas będzie zależało to, czy poprawnie zarządzimy operacjami blokującymi i nieblokującymi.

Parafrazując, zyskujemy od razu większą kontrolę nad sterowaniem zasobami naszej aplikacji, ale musimy pamiętać o tym i dostosowywać aplikację tam, gdzie to konieczne.

Dodajemy Reactive Routes do projektu

Żeby skorzystać z Quarkus Reactive Routes należy dodać je do projektu. Najprostszym rozwiązaniem jest wykonanie polecenia z linii komend.

./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-vertx-web"

Mały HINT. Najprostszym sposobem na dodanie nowego rozszerzenia do projektu jest skorzystanie ze strony https://code.quarkus.io. Na stronie możemy przefiltrować rozszerzenia, wybrać te, które nas interesuje i następnie skopiować komendę do wywołania z shella.

Quarkus dodawanie rozszerzeń z linii komend
Dodawanie rozszerzeń w Quarkus

Przykładowa usługa

Wyobraźmy sobie najprostszą usługę. Załóżmy, że ma ona zwracać pusty obiekt JSON. Jej kod mógłby wyglądać następująco:

@ApplicationScoped
public class QuarkusReactiveRoutes {

  @Route(path = "api/get-json", produces = MediaType.APPLICATION_JSON)
  void jsonResult(RoutingContext rc) {
    rc.response()
        .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
        .end("{}");
  }

}

Omówmy pokrótce ten kod:

Linia 1 to zastosowanie adnotacji CDI deklarującej scope naszego beana. Quarkus bazuje na CDI w związku z tym stusujemy adnotacje z tej specyfikacji. Powyższy przykład zadziałałby również, gdyby klasa nie była adnotowana, wówczas domyślnie zastosowana byłaby adnotacja @javax.inject.Singleton. Różnica byłaby zauważalna w bardziej złożonych aplikacjach. Tutaj warto tylko powiedzieć, że w przypadku zastosowania CDI powinno zostać utworzone proxy do naszego beana, a w przypadku @javax.inject.Singleton już nie. Jeśli chciałbyś dowiedzieć się więcej na temat scope w Quarkus to tutaj będzie dobre źródło: https://quarkus.io/guides/cdi-reference.

Linia 4 to zastosowanie adnotacji @Route, dzięki której Quarkus wie, że definiujemy Reactive Route. Konstrukcja tej adnotacji jest podobna do tych, które spotykamy w innych szkieletach.

Linia 5 to uzyskanie dostępu do kontekstu żądania i odpowiedzi. Dzięki RoutingContext możemy obsłużyć naszą operację biznesową, wiemy co jest na wejściu i ustawiamy wyjście.

Linia 7 dla osób przyzwyczajonych do Spring Boot wydawać się może dziwna. W Spring Boot dzięki automatycznej konfiguracji ObjectMappera z Jacksona mamy zapewnioną konwersję zwracanego typu do JSON i ustawienie odpowiedniego Content-Type odpowiedzi. W Quarkus nie jest to automatyczne zachowanie i należy tę sytuację w tym przypadku obsłużyć.

Gdybyśmy pominęli linię 7 wówczas nasza odpowiedź byłaby pozbawiona nagłówka Content-Type:

λ curl -i http://localhost:8080/api/get-json
HTTP/1.1 200 OK
content-length: 2

Dodanie tej linii powoduje zwrot poprawnego nagłówka:

λ curl -i http://localhost:8080/api/get-json
HTTP/1.1 200 OK
Content-Type: application/json
content-length: 2

Operacje nieblokujące i blokujące

Jeśli popatrzymy na adnotację @Route, to zobaczymy, że sterowanie typem operacji realizowane jest przez ustawienie type.

Domyślna wartość to HandlerType.NORMAL. Takie określenie może być ponownie nieco mylące dla osób przyzwyczajonych do tradycyjnych serwerów aplikacyjnych, gdzie za sytuację normalną przyjmuje się obsługę blokującą żadań.

W tym przypadku jest jednak inaczej. Adnotacja @Route pochodzi z Vert.x, a co za tym idzie sytuacja mająca miano „normalnej” oznacza nieblokujące wywołanie. Musimy o tym pamiętać, bo korzystanie rozwiązań blokujących w takiej sytuacji zniszczy nam wydajność aplikacji.

Oczywiście mamy możliwość zdefiniowania również operacji blokujących, które będą delegowane do innej puli wątków. Metoda oznaczona @Route z ustawieniem type na HandlerType.BLOCKING zapewni nam takie działanie.

Podsumowanie

W artykule przedstawiony został deklaratywny sposób pracy z rozszerzeniem Quarkus Reactive Routes. Dzięki podejściu zastosowanemu w tym rozwiązaniu programista zyskuje dużą kontrolę nad sposobem obsługi żądań przychodzących do aplikacji. Wpływa to na jej wydajność. Kosztem w tym przypadku pozostaje nieco większa złożoność kodu.

0 0 votes
Article Rating
Subscribe
Powiadom o
guest
0 komentarzy
Inline Feedbacks
View all comments
0
Jestem ciekawy, co myślisz. Dodaj komentarz na dole!x