DEV Community

Andrzej Korcz
Andrzej Korcz

Posted on

Korzyści z użycia wirtualnych wątków w aplikacji Spring Boot

Image description

Korzyści z użycia wirtualnych wątków w aplikacji Spring Boot są bardziej wyraźne w sytuacjach wymagających intensywnej współbieżności i asynchroniczności. Oto omówienie zalet i przykładów, gdzie wirtualne wątki mają sens:

Zalety Wirtualnych Wątków:

  1. Obsługa Wysokiej Liczby Współbieżnych Połączeń:

    • Tradycyjne wątki w Javie są zasobożerne — każde stworzenie wątku systemowego wymaga pamięci i zasobów CPU. Przy dużej liczbie jednoczesnych połączeń (np. setki tysięcy żądań HTTP do serwera) system zaczyna się przeciążać. Wirtualne wątki są znacznie lżejsze, ponieważ JVM może tworzyć miliony wirtualnych wątków, które zarządzane są w bardziej efektywny sposób.
    • W kontekście aplikacji webowych lub mikroserwisowych, które często obsługują tysiące jednoczesnych zapytań (HTTP, zapytania do bazy danych, zewnętrzne API), wirtualne wątki pozwalają na łatwiejsze skalowanie.
  2. Efektywniejsze Przetwarzanie I/O:

    • W aplikacjach Spring Boot wiele operacji opiera się na I/O (wejście-wyjście), np. odpytywanie baz danych, komunikacja z zewnętrznymi serwisami przez API, odczyt/zapis plików, przesyłanie danych po sieci. Operacje I/O są naturalnie blokujące i czekanie na odpowiedź może zablokować tradycyjny wątek systemowy.
    • Wirtualne wątki pozwalają na bardziej efektywne zarządzanie operacjami I/O, ponieważ mogą być łatwo zawieszane i wznawiane, gdy dane są dostępne, bez blokowania zasobów systemowych (np. CPU, pamięci).
  3. Prostszy Model Programowania:

    • Tradycyjnie w Javie do obsługi asynchronicznych operacji używało się wątków, puli wątków lub bardziej skomplikowanych mechanizmów, takich jak CompletableFuture, czy programowanie reaktywne (Reactor, RxJava). Chociaż te podejścia działają, mogą wprowadzać pewną złożoność i trudności z zarządzaniem współbieżnością.
    • Dzięki wirtualnym wątkom, programiści mogą używać bardziej intuicyjnych konstrukcji blokujących (takich jak Thread.sleep() czy klasyczne I/O), a JVM sam optymalizuje zarządzanie wątkami. Zyskujemy więc prostszy model programowania, zachowując efektywność współbieżności.
  4. Lepsza Skalowalność Aplikacji:

    • W przypadku serwisów mikroserwisowych czy serwerów HTTP, wirtualne wątki pozwalają na większą skalowalność bez konieczności przepisania aplikacji na programowanie reaktywne. Umożliwia to łatwe zwiększenie liczby jednoczesnych połączeń (przepustowości) przy mniejszym zużyciu zasobów.

Przykłady Użycia Wirtualnych Wątków:

Aby lepiej zrozumieć, gdzie wirtualne wątki mogą mieć sens, przyjrzyjmy się bardziej realistycznym scenariuszom:

1. Serwisy Mikroserwisowe (HTTP i Bazy Danych)

Załóżmy, że mamy serwis mikroserwisowy, który obsługuje wiele zapytań HTTP, a każde z tych zapytań odpytywało bazę danych oraz zewnętrzny serwis przez API. W takim przypadku tradycyjne wątki byłyby blokowane podczas oczekiwania na odpowiedzi z bazy danych oraz z zewnętrznego serwisu, co zmniejsza wydajność.

W przypadku wirtualnych wątków można napisać kod w sposób blokujący (czyli używać klasycznych operacji I/O), ale JVM nie będzie blokował systemowych zasobów, gdy wątek czeka na dane z bazy lub z sieci.

Przykład:

@GetMapping("/data")
public String fetchData() {
    try {
        // Wirtualny wątek pobiera dane z bazy
        String dbResult = databaseService.queryData(); // Operacja blokująca (I/O)

        // Wirtualny wątek pobiera dane z zewnętrznego serwisu
        String apiResult = externalApiService.fetchData(); // Operacja blokująca (I/O)

        return "Wynik: " + dbResult + ", " + apiResult;
    } catch (Exception e) {
        return "Błąd podczas przetwarzania danych!";
    }
}
Enter fullscreen mode Exit fullscreen mode

W tym przykładzie:

  • Tradycyjnie użycie blokujących zapytań do bazy danych i zewnętrznych API mogłoby wymagać reaktywnego programowania lub specjalnego zarządzania wątkami.
  • Wirtualne wątki pozwalają na blokujący kod bez wydajnościowych kompromisów — można efektywnie skalować aplikację bez dodawania złożoności.

2. Asynchroniczne Pobieranie Danych z Kilku Źródeł

Często w aplikacjach webowych konieczne jest pobranie danych z kilku różnych źródeł (np. API, bazy danych, innych serwisów) w odpowiedzi na jedno zapytanie użytkownika.

Z wirtualnymi wątkami możemy łatwo uruchomić te zapytania asynchronicznie, korzystając z prostego modelu wątków, bez potrzeby używania bardziej skomplikowanych mechanizmów jak CompletableFuture.

@GetMapping("/fetch-multiple")
public String fetchMultipleSources() {
    Executor executor = Executors.newVirtualThreadPerTaskExecutor();

    var future1 = CompletableFuture.supplyAsync(() -> service1.getData(), executor);
    var future2 = CompletableFuture.supplyAsync(() -> service2.getData(), executor);

    // Czekamy na wyniki obu operacji
    String result1 = future1.join();
    String result2 = future2.join();

    return "Wyniki: " + result1 + " oraz " + result2;
}
Enter fullscreen mode Exit fullscreen mode

Tutaj:

  • Każde zapytanie do serwisów (service1 i service2) uruchamiane jest w osobnym wirtualnym wątku.
  • W przypadku aplikacji mikroserwisowych, gdzie aplikacja często komunikuje się z wieloma zewnętrznymi serwisami, takie rozwiązanie pozwala na prostą obsługę dużej liczby równoczesnych połączeń, bez obciążenia zasobów systemu.

Podsumowanie

Wirtualne wątki w Javie 21 przynoszą ogromne korzyści w aplikacjach, które są mocno współbieżne i zależne od operacji I/O. Dzięki nim można:

  • Skalować aplikację do obsługi większej liczby jednoczesnych żądań przy niższym zużyciu zasobów.
  • Upraszczać kod współbieżny (mniej kodu reaktywnego lub zaawansowanego zarządzania wątkami).
  • Używać prostszego modelu programowania (blokującego), ale z efektywnością asynchronicznego wykonania.

Wirtualne wątki umożliwiają łatwe tworzenie aplikacji webowych czy mikroserwisów, które mogą obsługiwać więcej użytkowników i połączeń bez konieczności pisania złożonego kodu.

Top comments (0)