Jak działają serwlety? Tworzenie instancji, sesje, zmienne współdzielone i wielowątkowość

1143

Załóżmy, że mam serwer WWW, który zawiera wiele serwletów. Dla informacji przekazywanych między tymi serwletami ustawiam zmienne sesji i instancji.

Teraz, jeśli 2 lub więcej użytkowników wysyła żądanie do tego serwera, co stanie się ze zmiennymi sesji?
Czy wszystkie będą wspólne dla wszystkich użytkowników, czy będą różne dla każdego użytkownika?
Jeśli się różnią, to w jaki sposób serwer był w stanie odróżnić różnych użytkowników?

Jeszcze jedno podobne pytanie: jeśli nużytkownicy uzyskują dostęp do określonego serwletu, to ten serwlet jest tworzony instancji tylko za pierwszym razem, gdy pierwszy użytkownik uzyskał do niego dostęp, czy też jest on tworzony dla wszystkich użytkowników osobno?
Innymi słowy, co dzieje się ze zmiennymi instancji?

Ku Jon
źródło

Odpowiedzi:

1821

ServletContext

Kiedy kontener serwletu (jak Apache Tomcat ) uruchomi się, wdroży i załaduje wszystkie swoje aplikacje internetowe. Po załadowaniu aplikacji WWW kontener serwletu tworzy go ServletContextraz i przechowuje w pamięci serwera. Aplikacja internetowa użytkownika web.xml, a wszystkie zawarte web-fragment.xmljest analizowany pliki, a każdy <servlet>, <filter>a <listener>znalezione (lub każda klasa opatrzone @WebServlet, @WebFiltera @WebListenerodpowiednio) jest tworzony raz i przechowywane w pamięci serwera, jak również. Dla każdego filtrowanego instancji jego init()metoda jest wywoływana z nowym FilterConfig.

Kiedy a Servletma wartość <servlet><load-on-startup>lub @WebServlet(loadOnStartup)większą niż 0, wówczas jego init()metoda jest również wywoływana podczas uruchamiania z nową ServletConfig. Te serwlety są inicjowane w tej samej kolejności określonej przez tę wartość ( 11, 22 itd.). Jeśli ta sama wartość jest określona na więcej niż jednej serwletu, to każdy z tych serwletów jest umieszczony na tej samej kolejności, jak w web.xml, web-fragment.xmllub @WebServletclassloading. W przypadku braku wartości „ładuj przy uruchomieniu” init()metoda zostanie wywołana za każdym razem, gdy żądanie HTTP trafi do tego serwletu po raz pierwszy.

Kiedy kontener serwletu zostanie zakończony wszystkimi opisanymi powyżej krokami inicjalizacji, ServletContextListener#contextInitialized()zostanie wywołany.

Kiedy zamykają kontener serwletów w dół, to rozładowuje wszystkie aplikacje internetowe, wywołuje destroy()metodę wszystkich zainicjowanych serwletów i filtrów, a wszystko ServletContext, Servlet, Filtera Listenerprzypadki są zaśmiecone. W końcu ServletContextListener#contextDestroyed()zostanie wywołany.

HttpServletRequest i HttpServletResponse

Kontener serwletu jest podłączony do serwera WWW, który nasłuchuje żądań HTTP na określonym numerze portu (port 8080 jest zwykle używany podczas programowania, a port 80 w produkcji). Gdy klient (np użytkownik z poziomu przeglądarki internetowej, lub programowo przy użyciuURLConnection ) wysyła żądanie HTTP, kontener serwletów tworzy nowy HttpServletRequesti HttpServletResponseobiektów i przekazuje je poprzez dowolny zdefiniowany Filterw łańcuchu, a ostatecznie do Servletinstancji.

W przypadku filtrów The doFilter()metoda jest wywoływana. Gdy wywoływany jest kod kontenera serwletu chain.doFilter(request, response), żądanie i odpowiedź są kontynuowane do następnego filtru lub trafiają w serwlet, jeśli nie ma już pozostałych filtrów.

W przypadku serwletów The service()metoda jest wywoływana. Domyślnie ta metoda określa, na podstawie której z doXxx()metod należy wywołać request.getMethod(). Jeśli określona metoda jest nieobecna w serwlecie, w odpowiedzi zwracany jest błąd HTTP 405.

Obiekt żądania zapewnia dostęp do wszystkich informacji o żądaniu HTTP, takich jak adres URL, nagłówki, ciąg zapytania i treść. Obiekt odpowiedzi umożliwia kontrolowanie i wysyłanie odpowiedzi HTTP tak, jak chcesz, na przykład, umożliwiając ustawienie nagłówków i treści (zwykle z wygenerowaną treścią HTML z pliku JSP). Po zatwierdzeniu i zakończeniu odpowiedzi HTTP obiekty żądania i odpowiedzi są ponownie przetwarzane i udostępniane do ponownego użycia.

HttpSession

Gdy klient odwiedza aplikację internetową po raz pierwszy i / lub HttpSessionjest uzyskiwany po raz pierwszy request.getSession(), kontener serwletu tworzy nowy HttpSessionobiekt, generuje długi i unikalny identyfikator (który można uzyskać session.getId()) i zapisuje go w serwerze pamięć. Kontener serwletu ustawia również Cookiew Set-Cookienagłówku odpowiedzi HTTP JSESSIONIDjako nazwę i unikalny identyfikator sesji jako wartość.

Zgodnie ze specyfikacją plików cookie HTTP (umowa musi być przestrzegana przez każdą przyzwoitą przeglądarkę internetową i serwer), klient (przeglądarka internetowa) musi przesłać ten plik cookie z powrotem w kolejnych żądaniach w Cookienagłówku, dopóki plik cookie jest ważny ( tzn. unikalny identyfikator musi odnosić się do sesji, która nie wygasła, a domena i ścieżka są poprawne). Za pomocą wbudowanego monitora ruchu HTTP w przeglądarce możesz sprawdzić, czy plik cookie jest prawidłowy (naciśnij klawisz F12 w przeglądarce Chrome / Firefox 23+ / IE9 + i sprawdź kartę Sieć / Sieć ). Kontener serwletu sprawdzi Cookienagłówek każdego przychodzącego żądania HTTP pod kątem obecności pliku cookie z nazwą JSESSIONIDi użyje jego wartości (identyfikatora sesji), aby uzyskać powiązanie HttpSessionz pamięci serwera.

Że HttpSessionpozostaje przy życiu, dopóki nie zostanie bezczynności (nie używany w żądaniu) dłużej niż wartość limitu czasu określonego w <session-timeout>, w otoczeniu web.xml. Wartość limitu czasu domyślnie wynosi 30 minut. Tak więc, gdy klient nie odwiedza aplikacji internetowej dłużej niż określony czas, kontener serwletu niszczy sesję. Każde kolejne żądanie, nawet z określonym plikiem cookie, nie będzie już miało dostępu do tej samej sesji; kontener serwletu utworzy nową sesję.

Po stronie klienta plik cookie sesji pozostaje aktywny tak długo, jak długo działa instancja przeglądarki. Tak więc, jeśli klient zamknie instancję przeglądarki (wszystkie karty / okna), sesja zostanie zniszczona po stronie klienta. W nowej instancji przeglądarki plik cookie powiązany z sesją nie istniałby, więc nie byłby już wysyłany. Powoduje to HttpSessionutworzenie zupełnie nowego pliku cookie z sesją.

W skrócie

  • Trwa ServletContexttak długo, jak długo trwa aplikacja internetowa. Jest dzielony między wszystkie żądania we wszystkich sesjach.
  • Trwa HttpSessiontak długo, jak długo klient wchodzi w interakcję z aplikacją internetową z tą samą instancją przeglądarki, a sesja nie przekroczyła limitu czasu po stronie serwera. Jest dzielony między wszystkie żądania w tej samej sesji.
  • HttpServletRequestI HttpServletResponsena żywo od czasu serwlet odbiera żądania HTTP od klienta, aż do całkowitej odpowiedzi (strona internetowa) przyjechał. To jest nie podzielił się gdzie indziej.
  • Wszystko Servlet, Filteroraz Listenerprzypadki, żyć tak długo jak żyje to aplikacja internetowa. Są one dzielone między wszystkie żądania we wszystkich sesjach.
  • Wszelkie attributektóra jest zdefiniowana w ServletContext, HttpServletRequesti HttpSessionbędą żyć tak długo, jak obiekt w życiu zapytania. Sam obiekt reprezentuje „zakres” w ramach zarządzania komponentami, takimi jak JSF, CDI, Spring itp. Te struktury przechowują swoje fasole o attributeokreślonym zasięgu jako najbliższy pasujący zakres.

Bezpieczeństwo wątków

To powiedziawszy, twoim głównym problemem jest prawdopodobnie bezpieczeństwo wątków . Powinieneś teraz wiedzieć, że serwlety i filtry są wspólne dla wszystkich żądań. To miło, że Java jest wielowątkowa, a różne wątki (czytaj: żądania HTTP) mogą korzystać z tej samej instancji. Byłoby inaczej być zbyt drogie, aby odtworzyć, init()i destroy()je dla każdego wniosku.

Należy również zdawać sobie sprawę, że nigdy nie należy przypisywać żadnych danych o zasięgu żądania lub sesji jako zmiennej instancji serwletu lub filtru. Zostanie on udostępniony między wszystkimi innymi żądaniami w innych sesjach. To nie jest bezpieczne dla wątków! Poniższy przykład ilustruje to:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

Zobacz też:

BalusC
źródło
25
Więc kiedy w jakiś sposób dowiem się JSessionId, który jest wysyłany do klienta, mogę ukraść jego sesję?
Toskan
54
@Toskan: to prawda. Jest znany jako hack utrwalania sesji . Należy pamiętać, że nie dotyczy to JSP / Servlet. Wszystkie inne języki po stronie serwera, które utrzymują sesję przy pomocy pliku cookie, są również wrażliwe, takie jak PHP z PHPSESSIDplikiem cookie, ASP.NET z ASP.NET_SessionIDplikiem cookie itp. Dlatego też ;jsessionid=xxxnie jest mile widziane przepisywanie adresów URL za pomocą niektórych ram JSP / Servlet MVC. Upewnij się tylko, że identyfikator sesji nigdy nie jest ujawniany w adresie URL lub w inny sposób na stronach internetowych, aby nieświadomy użytkownik nie został zaatakowany.
BalusC
11
@Toskan: Upewnij się również, że twoja aplikacja internetowa nie jest wrażliwa na ataki XSS. Tj. Nie wyświetlaj ponownie żadnych danych kontrolowanych przez użytkownika w nieskalowanej formie. XSS otwiera drzwi do sposobów gromadzenia identyfikatorów sesji wszystkich użytkowników końcowych. Zobacz także Jaka jest ogólna koncepcja XSS?
BalusC,
2
@BalusC, przepraszam za moją głupotę. Oznacza to, że wszyscy użytkownicy mają dostęp do tej samej instancji thisIsNOTThreadSafe, prawda?
przyćmiony
4
@TwoThumbSticks 404 jest zwracany, gdy cały serwlet jest nieobecny. 405 jest zwracane, gdy serwlet jest obecny, ale żądana metoda doXxx () nie jest zaimplementowana.
BalusC,
428

Sesje

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Krótko mówiąc: serwer internetowy wydaje każdemu odwiedzającemu podczas pierwszej wizyty unikalny identyfikator . Odwiedzający musi przynieść ten dowód, aby mógł zostać rozpoznany następnym razem. Ten identyfikator pozwala również serwerowi poprawnie segregować obiekty należące do jednej sesji do drugiej.

Tworzenie instancji serwletu

Jeśli ładowanie przy uruchomieniu jest fałszywe :

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Jeśli ładowanie przy uruchomieniu jest prawdziwe :

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Gdy znajdzie się w trybie serwisowym i na groove, ten sam serwlet będzie działał na żądanie od wszystkich innych klientów.

wprowadź opis zdjęcia tutaj

Dlaczego nie jest dobrym pomysłem mieć jedną instancję na klienta? Pomyśl o tym: czy zatrudnisz jednego faceta od pizzy na każde zamówienie? Zrób to, a szybko znikniesz z biznesu.

Jednak wiąże się to z niewielkim ryzykiem. Pamiętaj: ten samotny facet ma w kieszeni wszystkie informacje o zamówieniach: więc jeśli nie jesteś ostrożny w kwestii bezpieczeństwa wątków serwletów , może skończyć na złym zamówieniu dla określonego klienta.

Jops
źródło
26
Twoje zdjęcie jest bardzo dobre dla mojego zrozumienia. Mam jedno pytanie: co zrobi ta restauracja pizzy, gdy pojawi się zbyt wiele zamówień na pizzę, wystarczy poczekać na jednego pizzę lub zatrudnić więcej pizzy? Dzięki .
zh18
6
to many requests at this moment. try again later
Odeśle
3
Serwlety, w przeciwieństwie do dostawców pizzy, mogą realizować więcej niż jedną dostawę jednocześnie. Muszą tylko zwrócić szczególną uwagę na to, gdzie zapisują adres klienta, smak pizzy ...
bruno
42

Sesja w serwletach Java jest taka sama jak sesja w innych językach, takich jak PHP. Jest unikalny dla użytkownika. Serwer może to śledzić na różne sposoby, takie jak pliki cookie, przepisywanie adresów URL itp. W tym artykule w dokumentacji Java wyjaśniono to w kontekście serwletów Java i wskazano, że dokładnie to, jak utrzymywana jest sesja, jest szczegółem implementacji pozostawionym projektantom serwera. Specyfikacja stanowi jedynie, że należy zachować ją jako unikalną dla użytkownika dla wielu połączeń z serwerem. Sprawdź ten artykuł Oracle, aby uzyskać więcej informacji na temat obu pytań.

Edytuj Jest tutaj doskonały samouczek dotyczący pracy z sesją wewnątrz serwletów. A oto rozdział firmy Sun dotyczący serwletów Java, czym są i jak z nich korzystać. Pomiędzy tymi dwoma artykułami powinieneś być w stanie odpowiedzieć na wszystkie pytania.

Chris Thompson
źródło
To rodzi dla mnie kolejne pytanie: Ponieważ istnieje tylko jeden kontekst serwletu dla całej aplikacji i uzyskujemy dostęp do zmiennych sesji za pośrednictwem tego kontekstu serwletu, więc w jaki sposób zmienne sesji mogą być unikalne dla każdego użytkownika? Dzięki ..
Ku Jon
1
jak uzyskujesz dostęp do sesji z servletContext? Nie masz na myśli servletContext.setAttribute (), prawda?
matt b
4
@KuJon Każda aplikacja internetowa ma jeden ServletContextobiekt. Ten obiekt ma zero, jeden lub więcej obiektów sesji - zbiór obiektów sesji. Każda sesja jest identyfikowana za pomocą pewnego rodzaju ciągu identyfikującego, co widać w kreskówkach z inną odpowiedzią. Ten identyfikator jest śledzony na kliencie przez cookie lub przepisywanie adresów URL. Każdy obiekt sesji ma własne zmienne.
Basil Bourque,
33

Kiedy kontener serwletu (jak Apache Tomcat) uruchomi się, będzie czytał z pliku web.xml (tylko jeden na aplikację), jeśli coś pójdzie nie tak lub wyświetli błąd w konsoli bocznej kontenera, w przeciwnym razie wdroży i załaduje całą sieć aplikacje przy użyciu pliku web.xml (tak nazwano go jako deskryptor wdrażania).

Podczas fazy tworzenia serwletu instancja serwletu jest gotowa, ale nie może obsłużyć żądania klienta, ponieważ brakuje jej dwóch informacji:
1: informacje kontekstowe
2: informacje o konfiguracji początkowej

Silnik serwletu tworzy obiekt interfejsu servletConfig kapsułkując w nim powyższe brakujące informacje. Wywołuje funkcję silnika serwletu init () serwletu, podając jako argument odwołania do obiektów servletConfig. Po całkowitym uruchomieniu init () serwlet jest gotowy do obsługi żądania klienta.

Q) Ile razy wystąpiła instancja i inicjalizacja serwletu?

A) tylko raz (dla każdego żądania klienta tworzony jest nowy wątek) tylko jedna instancja serwletu obsługuje dowolną liczbę żądań klienta, tj. Po obsłużeniu jednego serwera żądań klienta nie umiera. Oczekuje na inne żądania klienta, tj. Jaki CGI (dla każdego żądania klienta tworzony jest nowy proces), ograniczenie zostaje pokonane przez serwlet (wewnętrzny silnik serwletu tworzy wątek).

P) Jak działa koncepcja sesji?

A) ilekroć wywoływana jest metoda getSession () w obiekcie HttpServletRequest

Krok 1 : obiekt żądania jest oceniany pod kątem identyfikatora sesji przychodzącej.

Krok 2 : jeśli identyfikator nie jest dostępny, tworzony jest nowy obiekt HttpSession, a odpowiadający mu identyfikator sesji (tj. HashTable) identyfikator sesji jest zapisywany w obiekcie odpowiedzi httpservlet, a odwołanie do obiektu HttpSession jest zwracane do serwletu (doGet / doPost) .

Krok 3 : jeśli nie zostanie utworzony nowy dostępny obiekt sesji, identyfikator sesji jest pobierany z żądania, wyszukiwanie odbywa się w kolekcji sesji przy użyciu identyfikatora sesji jako klucza.

Po udanym wyszukiwaniu identyfikator sesji jest zapisywany w HttpServletResponse, a istniejące odwołania do obiektu sesji są zwracane do doGet () lub doPost () UserDefineservlet.

Uwaga:

1) gdy kontrola wychodzi z kodu serwletu do klienta, nie zapomnij, że obiekt sesji jest utrzymywany przez kontener serwletu, tj. Silnik serwletu

2) wielowątkowość jest pozostawiona programistom serwletów do implementacji, tj. Obsłużenia wielu żądań klienta, aby nie zawracać sobie głowy kodem wielowątkowym

Formularz skrócony:

Aplet jest tworzony podczas uruchamiania aplikacji (jest wdrażany w kontenerze serwletu) lub przy pierwszym dostępie (w zależności od ustawienia ładowania przy uruchamianiu), gdy aplet jest tworzony, wywoływana jest metoda init () apletu następnie serwlet (jego jedyna instancja) obsługuje wszystkie żądania (jego metoda service () jest wywoływana przez wiele wątków). Dlatego nie zaleca się synchronizacji i należy unikać zmiennych instancji serwletu, gdy aplikacja nie zostanie wdrożona (kontener serwletu się zatrzyma), wywoływana jest metoda destroy ().

Ajay Takur
źródło
20

Sesje - co powiedział Chris Thompson.

Tworzenie instancji - serwlet jest tworzony w momencie, gdy kontener odbierze pierwsze żądanie odwzorowane na serwlet (chyba że serwlet jest skonfigurowany do ładowania przy uruchamianiu z <load-on-startup>elementem w web.xml). To samo wystąpienie służy do obsługi kolejnych żądań.

Lauri Lehtinen
źródło
3
Poprawny. Dodatkowa myśl: Każde żądanie otrzymuje nowy (lub poddany recyklingowi) wątek do uruchomienia w tej pojedynczej instancji serwletu. Każdy serwlet ma jedną instancję i być może wiele wątków (jeśli jest wiele jednoczesnych żądań).
Basil Bourque,
13

Specyfikacja serwletu JSR-315 wyraźnie określa zachowanie kontenera WWW w metodach service (i doGet, doPost, doPut itp.) (2.3.3.1 Problemy z wielowątkowością, strona 9):

Kontener serwletu może wysyłać współbieżne żądania za pośrednictwem metody obsługi serwletu. Aby obsłużyć żądania, programista serwletów musi zapewnić odpowiednie warunki dla równoczesnego przetwarzania z wieloma wątkami w metodzie usługi.

Chociaż nie jest to zalecane, alternatywą dla programisty jest implementacja interfejsu SingleThreadModel, który wymaga od kontenera zagwarantowania, że ​​w metodzie usługi jest tylko jeden wątek żądania na raz. Kontener serwletu może spełnić to wymaganie poprzez serializację żądań serwletu lub przez utrzymanie puli instancji serwletu. Jeśli serwlet jest częścią aplikacji sieci Web, która została oznaczona jako dystrybuowana, kontener może utrzymywać pulę instancji serwletu w każdej maszynie JVM, w której aplikacja jest dystrybuowana.

W przypadku serwletów niewdrożących interfejsu SingleThreadModel, jeżeli metoda usługi (lub metody takie jak doGet lub doPost, które są wysyłane do metody usługi klasy abstrakcyjnej HttpServlet) zostały zdefiniowane za pomocą synchronizowanego słowa kluczowego, kontener serwletów nie może korzystać z metody puli instancji , ale musi serializować żądania za jego pośrednictwem. Zdecydowanie zaleca się, aby w takich okolicznościach programiści nie synchronizowali metody usługi (lub metod do niej wysyłanych) ze względu na szkodliwy wpływ na wydajność

tharindu_DG
źródło
2
FYI, bieżąca specyfikacja serwletu (2015-01) to 3.1, zdefiniowana przez JSR 340 .
Basil Bourque,
1
Bardzo zgrabna odpowiedź! @tharindu_DG
Tom Taylor
0

Jak wynika z powyższych wyjaśnień, poprzez wdrożenie modelu SingleThreadModel serwletowi można zapewnić bezpieczeństwo wątków dzięki kontenerowi serwletów. Implementacja kontenera może to zrobić na 2 sposoby:

1) Serializacja żądań (kolejkowanie) do pojedynczej instancji - jest to podobne do serwletu NIE implementującego SingleThreadModel, ALE synchronizującego metody service / doXXX; LUB

2) Tworzenie puli instancji - co jest lepszą opcją i kompromisem między uruchomieniem / inicjalizacją / czasem serwletu w porównaniu z ograniczającymi parametrami (pamięć / czas procesora) środowiska obsługującego serwlet.

Mahesh Balasubramanian
źródło
-1

Nie. Serwlety niebezpieczne dla wątków

Umożliwia to dostęp do więcej niż jednego wątku na raz

jeśli chcesz, aby serwlet był bezpieczny jako wątek, możesz przejść na

Implement SingleThreadInterface(i) który jest pustym interfejsem nie ma

metody

lub możemy przejść do metod synchronizacji

możemy wykonać całą usługę jako zsynchronizowaną za pomocą synchronizacji

słowo kluczowe przed metodą

Przykład::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

lub możemy umieścić blok kodu w bloku zsynchronizowanym

Przykład::

Synchronized(Object)

{

----Instructions-----

}

Uważam, że synchronizowany blok jest lepszy niż tworzenie całej metody

Zsynchronizowane

Ved Prakash
źródło