Spring Boot 2 oddał w ręce programistów nowy moduł umożliwiający programowanie reaktywne jakim jest – Spring WebFlux. Ponadto w szeregu nowych funkcjonalności jakie pojawiły się w tym rozwiązaniu znajduje się nowy reaktywny klient HTTP – WebClient.
Ogromną przewagą WebClient’a jest możliwość nawiązywania połączenia zarówno z usługami reaktywnymi jak i klasycznymi.
RestTemplate – klient blokujący
Podstawowym, bardzo popularnym klientem, który umożliwia na automatycznie mapowaniu typów jest RestTemplate – klient HTTP dostarczanym w Spring Web.
Jednak RestTemplate w swojej implementacji bazuje na Java Servlet API, który pracuje w modelu thread-per-request. Niestety w praktyce oznacza to, że będzie on blokował wątek do czasu otrzymania odpowiedzi. Innymi słowy RestTemplate nie sprawdzi się w środowisku reaktywnym gdyż jest klientem synchronicznym.
WebClient – asynchroniczny klient HTTP w Spring
Przede wszystkim WebClient bazuje na implementacji Reactor Netty, który jest nieblokującym, asynchronicznym rozwiązaniem. Dlatego umożliwia on na utworzenie zadania dla każdego zdarzenia. Przede wszystkim możemy napisać kod klienta dla reaktywnego API , które będzie przyjmowało odpowiedź zgodną z typami reaktywnymi (Mono i Flux).
Tak jak wspomniałem nie ma żadnych przeciwwskazań, aby używać tego rozwiązania w kontekście rozwiązań niereaktywnych. WebClient równie dobrze sprawdzi się jako klient klasycznych API.
WebClient – przykład implementacji
Do projektu należy dodać zależność, która będzie wskazywała na Spring WebFlux
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
Na potrzeby przykładu utworze proste reaktywne API, które będzie produkowało strumień przechowujących obiekty klasy Student:
@RestController public class StudentApi { @GetMapping(produces = MediaType.APPLICATION_STREAM_JSON_VALUE) public Flux<Student> get() { return Flux.just( new Student("Janek", "Dzbanek"), new Student("Ania", "Łania"), new Student("Łukasz", "Kuchasz"), new Student("Justyna", "Chityna")); } }
Potem wykorzystamy możliwości dostarczane przez WebClient’a. Jego implementacja, umożliwia pobranie danych z powyższego, reaktywnego API:
Logger logger = LoggerFactory.getLogger(Client.class); @EventListener(ApplicationReadyEvent.class) public Flux<Student> getStudents() { Flux<Student> studentFlux = WebClient.create() .get() .uri("http://localhost:8080") .retrieve() .bodyToFlux(Student.class); studentFlux.subscribe(student -> logger.info(student.toString())); return studentFlux; }
W pierwszej kolejności tworzymy nowy obiekt z wykorzystaniem wzorca builder. Następnie podajemy kolejno metodę HTTP i adres usługi. Ponadto dodajemy sposób pobierania danych i tutaj z pomocą przychodzą dwie metody:
- retrieve – która umożliwia na pobranie body;
- exchange – oprócz body dostarczająca dodatkowo informacje na temat statusu HTTP i nagłówka.
W tym przypadku zastosowałem retrieve, uzupełniając go metodą bodyToFlux, która przemapowała odpowiedź na reaktywny strumień danych przechowujący obiekty Student.
Podsumowanie – który klient HTTP?
Jednak wybór WebClient czy RestTemplate jest tylko kwestią profilu naszego projektu i zależności jakich w nim stosujemy. Znacznie lepiej jest korzystać z nowego klienta, ale uwzględnianie dodatkowej biblioteki do WebClienta tylko po to, aby móc z niego korzystać mija się z celem.
Kod źródłowy znajdziesz w moim repozytorium GitHub. Daj znać czy reaktywna serja Cie interesuje i chcesz wiecej artykułów tej treści 🙂