Embedded container nie taki doskonały
Kilka dni temu zachwalałem framework Spring Boot za to, że nie wymaga dodatkowego serwera do tego, aby nasza aplikacja się uruchomiła; że radzi sobie doskonale z serwerem wbudowanym w aplikację. Czy zamierzam się z tego wycofać? Nie. Dalej uważam, że embedded container w Spring Boot ułatwia development aplikacji uwalniając nas od konieczności utrzymywania i konfiguracji dodatkowego serwera.
Takie rozwiązanie jednak ma również pewne ograniczenia. Nie postawimy np. na jednym serwerze więcej niż jednej aplikacji (chyba że nasłuchiwałyby na różnych portach, ale to dość toporne rozwiązanie). Możliwości konfiguracyjne takiego serwera będą ubogie. Różnego rodzaju usługi w chmurze nie zawsze dają możliwość swobodnego uruchamiania plików .jar, więc tu też mógłby się pojawić problem.
Wygląda na to, że mogą wystąpić również problemy ze stabilnością. Taki przykład z życia: podczas pierwszej naszej próby uruchomienia naszej aplikacji na serwerze OpenShifta, odpalaliśmy standardowo jara, jednak aplikacja w nieznanych okolicznościach przestawała działać po jakimś czasie. Po wdrożeniu tej samej aplikacji na serwer WildFly również na OpenShifcie, w takiej samej konfiguracji sprzętowej, działa stabilnie już od jakiegoś czasu.
Z czasem może przyjść zatem czas, kiedy wbudowany serwer (w szczególności na środowisku produkcyjnym) nie będzie już odpowiednim rozwiązaniem. Dzisiaj więc zobaczymy, jak przygotować naszą aplikację (znów posłużymy się aplikacją z poprzednich postów) do deploymentu na zewnętrzny serwer
Co trzeba zrobić?
Nie jest to trudne, ale jest parę rzeczy, które musimy wziąć pod uwagę, jeśli chcemy uniknąć problemów:
- aplikacja musi być zapakowana w plik .war
- musimy pozbyć się wbudowanego serwera. W przeciwnym razie aplikacja na zewnętrznym serwerze próbuje robić jakieś dziwactwa związane z serwerem wbudowanym, co oczywiście kończy się katastrofą
- wszelka dodatkowa inicjalizacja zawarta w metodzie main() musi zostać przeniesiona w inne miejsce (zaraz napiszę gdzie). Metoda main() nie zostanie wywołana na zewnętrznym serwerze
Pakowanie aplikacji do .war
To najprostszy krok. Wystarczy do naszego pliku pom.xml dodać linijkę <packaging>war</packaging>. Wtedy plik ten powinien wyglądać tak:
war 4.0.0 com.example SpringBootFirstApp 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-parent 1.2.3.RELEASE config org.springframework.boot spring-boot-maven-plugin org.springframework.boot spring-boot-starter-web
Usunięcie wbudowanego serwera
Kwestia prawie tak samo prosta, gdyż znów ogranicza się do edycji pliku pom.xml (tym razem jednakże, będzie trzeba dodać więcej linijek :) ). Robi się to przez dodanie zależności serwera tomcat i ustawienie jej zakresu (scope'u) jako provided - dzięki temu Maven nie zapakuje nam bibliotek odpowiedzialnych za serwer. Wynikowy pom.xml będzie wyglądał następująco:
I tyle!war 4.0.0 com.example SpringBootFirstApp 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-parent 1.2.3.RELEASE config org.springframework.boot spring-boot-maven-plugin org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat provided
Inicjalizacja aplikacji
Tutaj już nie jest tak banalnie, ale nadal nienajgorzej. Aby nasza aplikacja działała prawidłowo na zewnętrznym serwerze, również musi mieć jakiś punkt wejścia (entry-point). Wcześniej była to metoda main(). Jak będzie teraz? Otóż przy uruchamianiu Spring Boot wyszuka klasy typu SpringBootServletInitializer i wywoła jej metodę protected SpringApplicationBuilder configure(SpringApplicationBuilder application). Typowo chcemy mieć klasę, która dziedziczy z klasy SpringBootServletInitializer i przesłania (override) metodę oryginalną. Taka metoda jest właśnie miejscem na wszelkie działania inicjalizacyjne, które mają mieć miejsce tuż po uruchomieniu aplikacji.
W naszym przykładzie zmofyfikujemy klasę MainClass, aby tak jak wcześniej, od niej rozpoczynało się działanie aplikacji. Po wspomnianych zmianach będzie wyglądać tak:
package com.example; import java.lang.Override; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.boot.builder.SpringApplicationBuilder; @EnableConfigurationProperties @SpringBootApplication public class MainClass extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { //inicjalizacja aplikacji System.out.println("Inicjalizuję się..."); return application.sources(MainClass.class); } public static void main(String[] args) { SpringApplication.run(MainClass.class, args); } }
Co właściwie możemy inicjalizować? Np. jeśli nasza aplikacja korzysta z bazy danych po JDBC, konieczne pewnie będzie zarejestrowanie sterownika bazy. Wbudowany w aplikację Tomcat nie ma problemu, jeśli taki sterownik nie zostanie zarejestrowany, natomiast zewnętrzny sypał błędami, jeśli nie zostało to zrobione. Taką rejestrację można wykonać następującym kawałkiem kodu w metodzie configure:
try { DriverManager.registerDriver(new Driver()); } catch(SQLException e) { throw new RuntimeException(e); }
Budowa aplikacji
Ze zmian to tyle, czas zbudować naszą aplikację. W tym celu oczywiście wchodzimy do katalogu z plikiem pom.xml i wykonujemy komendę mvn clean install. Budowanie powinno się udać i powinniśmy zauważyć, że w katalogu target utworzone zostały dwa pliki:
- SpringBootFirstApp-0.0.1-SNAPSHOT.war
- SpringBootFirstApp-0.0.1-SNAPSHOT.war.original
Zawsze należy do deploymentu używać pliku .original. Ten drugi posiada też spakowane biblioteki wbudowanego Tomcata.
Tak przygotowany plik .war możemy wdrożyć na nasz ulubiony serwer.
Brak komentarzy:
Prześlij komentarz