Як працюють серветки? Моменталізація, сеанси, загальні змінні та багатопотоковість


1144

Припустимо, у мене є веб-сервер, який вміщує численні сервлети. Для передачі інформації серед цих сервлетів я встановлюю змінні сеансу та екземпляра.

Тепер, якщо 2 або більше користувачів надсилають запит на цей сервер, що робити з змінними сеансу?
Чи всі вони будуть спільними для всіх користувачів або вони будуть різними для кожного користувача?
Якщо вони різні, то як сервер зміг розмежувати різних користувачів?

Ще одне подібне запитання: якщо є nкористувачі, які отримують доступ до певного сервлету, то цей сервлет отримує екземпляр лише в перший раз, коли перший користувач звернувся до нього або він отримує інстанціювання для всіх користувачів окремо?
Іншими словами, що відбувається зі змінними екземпляра?

Відповіді:


1821

ServletContext

Коли контейнер сервлетів (наприклад, Apache Tomcat ) запускається, він розгорнуть і завантажить усі свої веб-програми. Коли веб-додаток завантажується, контейнер сервлетів створює ServletContextодин раз і зберігає його в пам'яті сервера. Веб - додаток web.xmlі все включено web-fragment.xmlфайли аналізуються, і кожне <servlet>, <filter>і <listener>знайшов (або кожен клас з анотацією @WebServlet, @WebFilterі @WebListenerвідповідно) конкретизується раз і зберігається в пам'яті сервера , а також. Для кожного створеного фільтра його init()метод викликається новим FilterConfig.

Якщо у Servleta <servlet><load-on-startup>або @WebServlet(loadOnStartup)значення більше ніж 0, тоді його init()метод також викликається під час запуску з новим ServletConfig. Ці сервлети ініціалізуються в тому ж порядку, визначеному цим значенням ( 11-е, 22-е і т.д.). Якщо ж значення задано більше одного сервлету, то кожен з цих сервлет завантажуються в тому ж порядку , як вони з'являються в web.xml, web-fragment.xmlабо @WebServletзавантаженні класів. У разі відсутності значення "завантаження при запуску" init()метод буде викликатись, коли запит HTTP потрапляє в цей сервлет вперше.

Коли контейнер сервлета буде закінчений з усіма описаними вище етапами ініціалізації, тоді ServletContextListener#contextInitialized()буде викликано заклик.

Коли сервлет контейнер закривається вниз, він вивантажує всі веб - додатки, викликає destroy()метод всіх його ініціалізувати сервлетів і фільтрів, а також всі ServletContext, Servlet, Filterі Listenerекземпляри громив. Нарешті ServletContextListener#contextDestroyed()заповіт буде застосовано.

HttpServletRequest та HttpServletResponse

Контейнер сервлетів приєднаний до веб-сервера, який слухає HTTP-запити на певний номер порту (порт 8080 зазвичай використовується під час розробки, а порт 80 у виробництві). Коли клієнт (наприклад, користувач із веб-браузером або програмно використовуючимURLConnection ) надсилає HTTP-запит, контейнер сервлетів створює нові HttpServletRequestта HttpServletResponseоб'єкти та передає їх через будь-який визначений Filterланцюг і, зрештою, Servletекземпляр.

Що стосується фільтрів , doFilter()метод викликається. Коли код контейнера сервлетів дзвонить chain.doFilter(request, response), запит і відповідь продовжуються до наступного фільтра або натискають сервлет, якщо немає інших фільтрів.

У разі сервлетів використовується service()метод. За замовчуванням цей метод визначає, який із doXxx()методів викликати на основі request.getMethod(). Якщо визначений метод відсутній у сервлета, то у відповіді повертається помилка HTTP 405.

Об'єкт запиту забезпечує доступ до всієї інформації про HTTP-запит, такі як його URL, заголовки, рядок запиту та тіло. Об'єкт відповіді надає можливість керувати та надсилати відповідь HTTP так, як вам потрібно, наприклад, дозволяє встановлювати заголовки та тіло (як правило, з генерованого вмісту HTML із файлу JSP). Коли відповідь HTTP введено та закінчено, об'єкти запиту та відповіді рециркулюються та стають доступними для повторного використання.

HttpSession

Коли клієнт відвідує веб-сервер вперше та / або HttpSessionотримує його вперше через request.getSession(), контейнер сервлетів створює новий HttpSessionоб'єкт, генерує довгий і унікальний ідентифікатор (який ви можете отримати session.getId()) і зберігає його в сервері пам'ять. Контейнер сервлетів також встановлює Cookieв Set-Cookieзаголовку відповіді HTTP з JSESSIONIDйого іменем та унікальним ідентифікатором сеансу як своїм значенням.

Відповідно до специфікації файлів cookie HTTP (у договорі повинен дотримуватися будь-який пристойний веб-браузер та веб-сервер), клієнт (веб-браузер) зобов'язаний відправити це cookie назад у наступних запитах у Cookieзаголовку, доки файл cookie дійсний ( тобто унікальний ідентифікатор повинен посилатися на сеанс, який не закінчився, і домен і шлях є правильними). Використовуючи вбудований монітор HTTP-трафіку вашого браузера, ви можете переконатися, що файл cookie є дійсним (натисніть F12 в Chrome / Firefox 23+ / IE9 + і перевірте вкладку Net / Network ). Контейнер сервлетів перевіряє Cookieзаголовок кожного вхідного запиту HTTP на наявність файлу cookie з ім'ям JSESSIONIDта використовує його значення (ідентифікатор сесії) для отримання асоційованого HttpSessionз пам'яті сервера.

В HttpSessionзалишається живий , поки він знаходиться в режимі очікування (тобто не використовується в запиті) більше , ніж значення тайм - ауту , зазначених в <session-timeout>, настройки в web.xml. Значення затримки за замовчуванням становить 30 хвилин. Отже, коли клієнт не відвідує веб-доданок довше зазначеного часу, контейнер сервлетів переносить сеанс. Кожен наступний запит, навіть із вказаним файлом cookie, більше не матиме доступу до тієї ж сесії; контейнер сервлетів створить новий сеанс.

На стороні клієнта файли cookie сеансу залишаються живими, поки працює екземпляр браузера. Отже, якщо клієнт закриває екземпляр браузера (усі вкладки / вікна), сеанс переноситься на сторону клієнта. У новому екземплярі веб-переглядача файл cookie, пов’язаний із сеансом, не існував, тому він більше не надсилався. Це спричиняє створення абсолютно нового HttpSessionфайлу cookie сеансу.

Коротко

  • У ServletContextжитті так довго , як веб - додаток життя. Він ділиться між усіма запитами на всіх сесіях.
  • У HttpSessionжитті до тих пір , поки клієнт взаємодіє з веб - додаток з тим же примірником браузера, і сеанс не минуло на стороні сервера. Він поділяється між усіма запитами в одному сеансі.
  • HttpServletRequestІ HttpServletResponseжити з моменту сервлет отримує запит HTTP від клієнта, поки повну відповідь (веб - сторінки) не надходило. Він не поділений ніде.
  • Все Servlet, Filterі Listenerекземпляри живуть до тих пір , як веб - додаток живе. Вони поділяються між усіма запитами на всіх сесіях.
  • Будь-яке, attributeщо визначене в ServletContext, HttpServletRequestі HttpSessionбуде жити до тих пір, поки живе даний предмет. Сам об'єкт являє собою «сферу» в рамках управління бобом , такі як JSF, CDI, Spring і т.д. Ці структури зберігати свої Scoped бобів в якості attributeйого найближчого відповідності обсягу.

Безпека нитки

Однак, ваша головна проблема - це, можливо, безпека ниток . Тепер ви повинні знати, що сервлети та фільтри поділяються між усіма запитами. Це приємна річ у Java, її багатопотоковість і різні потоки (читайте: HTTP-запити) можуть використовувати один і той же екземпляр. Інакше відтворити їх було б занадто дорого, init()і destroy()їх для кожного запиту.

Ви також повинні усвідомити, що ніколи не слід призначати будь-які запити чи дані, що охоплюють сеанс, як змінну екземпляра сервлета чи фільтра. Їм буде надано доступ до всіх інших запитів на інших сесіях. Це не безпечно для ниток! Наведений нижче приклад ілюструє це:

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.
    } 
}

Дивись також:


25
Отже, коли я якось можу дізнатися JSessionId, який отримує надсилання клієнту, я можу вкрасти його сеанс?
Тоскан

54
@Toskan: це правильно. Він відомий як виправлення сеансу . Зверніть увагу, що це не стосується JSP / Servlet. Усі інші серверні мови, які підтримують сеанс файлом cookie, також чутливі, як PHP з PHPSESSIDфайлом cookie, ASP.NET з ASP.NET_SessionIDфайлом cookie та ін. Ось чому, тому переписування URL-адрес за допомогою ;jsessionid=xxxавтоматичних фреймворків JSP / Servlet MVC автоматично нахмурене. Просто переконайтеся, що ідентифікатор сеансу ніколи не відображається в URL-адресі чи іншими способами на веб-сторінках, щоб не напав на невідомий кінцевий користувач.
BalusC

11
@Toskan: Також переконайтеся, що ваш webbapp не чутливий до XSS-атак. Тобто не повторно відображати будь-який керований користувачем вхід у незмінній формі. XSS відкрив двері для способів збору ідентифікаторів сеансу всіх користувачів. Дивіться також Яка загальна концепція XSS?
BalusC

2
@BalusC, вибач за мою дурість. Це означає, що всі користувачі отримують доступ до одного і того ж примірника цьогоIsNOTThreadSafe, правда?
затьмарити

4
@TwoThumbSticks 404 повертається, коли весь сервлет відсутній. 405 повертається, коли сервлет присутній, але бажаний метод doXxx () не реалізований.
BalusC

428

Сесії

введіть тут опис зображення введіть тут опис зображення

Якщо коротко: веб-сервер видає унікальний ідентифікатор кожному відвідувачеві під час його першого відвідування. Відвідувач повинен повернути цей ідентифікатор, щоб його впізнали наступного разу. Цей ідентифікатор також дозволяє серверу належним чином відокремити об'єкти, що належать одному сеансу, проти іншої.

Servlet Instantiation

Якщо навантаження на старті є помилковим :

введіть тут опис зображення введіть тут опис зображення

Якщо навантаження на старті є істинним :

введіть тут опис зображення введіть тут опис зображення

Після того, як він перейде в сервісний режим і на канавку, той же сервлет працюватиме над запитами всіх інших клієнтів.

введіть тут опис зображення

Чому не годиться мати один примірник на клієнта? Подумайте над цим: чи наймете ви одного піца для кожного замовлення, що надійшло? Зробіть це, і ви не зможете зайняти роботу за короткий час.

Хоча це має невеликий ризик. Пам'ятайте: цей єдиний хлопець зберігає всю інформацію про замовлення в кишені: тому, якщо ви не будете обережні щодо безпеки потоку на сервлетах , він може в кінцевому підсумку подати неправильне замовлення певному клієнту.


26
Ваша картина дуже гарна для мого розуміння. У мене є одне запитання: Що робитиме цей піцар, коли надходить занадто багато замовлень піци, просто зачекайте одного хлопця піци чи наймайте більше хлопця піци? Дякую .
z18 18

6
Він поверне повідомлення зto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords

3
Сервлети, на відміну від людей, які доставляють піцу, можуть одночасно робити більше, ніж одну доставку. Їм просто слід особливо подбати про те, де вони записують адресу клієнта, аромат піци ...
bruno

42

Сесія в сервлетах Java така ж, як сесія в інших мовах, таких як PHP. Це унікально для користувача. Сервер може відстежувати це різними способами, такими як файли cookie, перезапис URL-адрес тощо. Ця стаття в документі Java пояснює це в контексті сервлетів Java і вказує, що саме те, як підтримується сеанс, є детальною інформацією про реалізацію, яку залишили дизайнери сервера. Специфікація лише передбачає, що вона повинна підтримуватися як унікальна для користувача через безліч з'єднань із сервером. Перегляньте цю статтю від Oracle, щоб отримати докладнішу інформацію щодо обох питань.

Edit Існує відмінний підручник тут про те , як працювати з сеансом всередині сервлет. І ось це глава з Сонця про сервлетів, що вони і як використовувати їх. Між цими двома статтями ви повинні мати можливість відповісти на всі ваші запитання.


Це викликає ще одне питання для мене, оскільки існує лише один контекст сервлету для всієї програми, і ми отримуємо доступ до змінних сеансів через цей сервлеттекст, тож як змінні сеансу можуть бути унікальними для кожного користувача? Дякую ..
Ku Jon

1
як ви отримуєте доступ до сеансу через серверContext? Ви не посилаєтесь на servletContext.setAttribute (), чи не так?
мат b

4
@KuJon Кожен веб-додаток має один ServletContextоб’єкт. У цього об’єкта є нуль, один або більше сеансових об’єктів - колекція сесійних об’єктів. Кожен сеанс ідентифікується певним рядком ідентифікатора, як це видно в мультфільмах на інші відповіді. Цей ідентифікатор відстежується на клієнті за допомогою файлу cookie або перезапису URL-адреси. Кожен об’єкт сеансу має свої змінні.
Василь Бурк

33

Коли запуститься контейнер сервлетів (наприклад, Apache Tomcat), він зчитує з файлу web.xml (лише один на додаток), якщо щось піде не так або з’явиться помилка на консолі бічної частини контейнера, інакше він розгорне та завантажить всю мережу програми, використовуючи web.xml (так його назвали як дескриптор розгортання).

Під час фази екземпляра сервлета екземпляр сервлета готовий, але він не може обслуговувати запит клієнта, оскільки він відсутній з двома відомостями:
1: інформація про контекст
2: початкова інформація про конфігурацію

Сервлет-двигун створює об'єкт інтерфейсу servletConfig, інкапсулюючи вищезазначену пропущену інформацію в нього, серверний двигун викликає init () сервлета, надаючи в якості аргументу посилання на об’єкт servletConfig. Після того, як init () повністю виконаний, сервлет готовий обслуговувати запит клієнта.

Q) За життя сервлета, скільки разів відбувається інстанція та ініціалізація ??

A) лише один раз (для кожного запиту клієнта створюється новий потік) лише один екземпляр сервлета обслуговує будь-яке число клієнтського запиту, тобто після обслуговування одного сервера запитів клієнта не вмирає. Він чекає на інші запити клієнта, тобто те, що CGI (для кожного запиту клієнта створюється новий процес) обмеження долається сервлетом (внутрішній сервлет-движок створює нитку).

Q) Як працює концепція сеансу?

A) кожного разу, коли виклик getSession () на HttpServletRequest об'єкт

Крок 1. Об'єкт запиту оцінюється для ідентифікатора вхідного сеансу.

Крок 2 : якщо ідентифікатор недоступний, створюється абсолютно новий HttpSession об'єкт і генерується його відповідний ідентифікатор сеансу (тобто HashTable) ідентифікатор сеансу зберігається в об'єкт відповіді httpservlet, а посилання на HttpSession об'єкт повертається в сервлет (doGet / doPost) .

Крок 3 : якщо доступний ідентифікатор абсолютно нового об'єкта сеансу не створений, ідентифікатор сеансу вибирається з об'єкта запиту, пошук здійснюється в колекції сеансів, використовуючи ідентифікатор сесії як ключ.

Після успішного пошуку ідентифікатор сеансу зберігається у HttpServletResponse, а існуючі посилання на об’єкт сеансу повертаються до doGet () або doPost () UserDefineservlet.

Примітка:

1) коли управління відходить від сервлет-коду до клієнта, не забувайте, що об'єкт сеансу утримується контейнером сервлетів, тобто сервлет-двигуном

2) багатопотоковість залишається розробникам сервлетів для впровадження, тобто, обробляти кілька запитів клієнта, нічого не турбуючи про багатопотоковий код

Коротка форма:

Сервлет створюється під час запуску програми (вона розгорнута в контейнері сервлетів) або коли вона вперше отримує доступ (залежно від налаштування завантаження при запуску), коли сервлет створюється миттєво, називається метод серветта init (). тоді сервлет (його єдиний екземпляр) обробляє всі запити (його метод () метод викликається декількома потоками). Ось чому не доцільно здійснювати синхронізацію в ньому, і вам слід уникати змінних екземплярів сервлета, коли додаток не використовується, (контейнер сервлетів зупиняється), викликається метод kill ().


20

Сеанси - те, що сказав Кріс Томпсон.

Instantiation - сервлет створюється в момент, коли контейнер отримує перший запит, відображений на сервлет (якщо тільки сервлет не налаштований для завантаження при запуску з введеним <load-on-startup>елементом web.xml). Цей же екземпляр використовується для подачі подальших запитів.


3
Правильно. Додаткова думка: Кожен запит отримує новий (або перероблений) потік для запуску на цьому єдиному екземплярі сервлетів. У кожного сервлета є один екземпляр і, можливо, багато потоків (якщо багато одночасних запитів).
Василь Бурк

13

Специфікація сервлетів JSR-315 чітко визначає поведінку веб-контейнерів у сервісі (і методах doGet, doPost, doPut тощо) (2.3.3.1. Багатопотокові випуски, стор. 9):

Контейнер сервлетів може надсилати одночасні запити за допомогою сервісного методу сервлета. Для обробки запитів розробник сервлетів повинен передбачити відповідні умови для одночасної обробки з декількома потоками в сервісному методі.

Хоча це не рекомендується, альтернативою для Developer є реалізація інтерфейсу SingleThreadModel, який вимагає, щоб контейнер гарантував, що в методі обслуговування одночасно є лише один потік запиту. Контейнер сервлетів може задовольнити цю вимогу шляхом серіалізації запитів на сервлет або шляхом підтримки пулу екземплярів сервлетів. Якщо сервлет є частиною веб-програми, яка була позначена як розповсюджувана, контейнер може підтримувати пул екземплярів сервлетів у кожному JVM, через який програма поширюється.

Для сервлетів, що не реалізують інтерфейс SingleThreadModel, якщо спосіб обслуговування (або такі методи, як doGet або doPost, які пересилаються до методу обслуговування абстрактного класу HttpServlet) був визначений за допомогою синхронізованого ключового слова, контейнер сервлета не може використовувати підхід пулу примірника , але має серіалізувати запити через нього. Настійно рекомендується, щоб розробники не синхронізували метод обслуговування (або надіслані до нього методи) в цих умовах через згубний вплив на продуктивність


2
FYI, поточна специфікація сервлетів (2015-01) - 3.1, визначена JSR 340 .
Василь Бурк


1
Дуже акуратна відповідь! @tharindu_DG
Том Тейлор

0

Як зрозуміло з вищезгаданих пояснень, реалізуючи SingleThreadModel , сервлет може бути забезпечений безпекою потоку контейнером сервлетів. Реалізація контейнера може зробити це двома способами:

1) Серіалізація запитів (у черзі) до одного екземпляра - це схоже на сервлет, що НЕ реалізує SingleThreadModel, АЛЕ синхронізує методи служби / doXXX; АБО

2) Створення пулу екземплярів - що є кращим варіантом і компромісом між завантажувальним / ініціалізаційним зусиллям / часом сервлета на відміну від обмежувальних параметрів (пам'ять / час процесора) середовища, що розміщує сервлет.


-1

Ні. Сервлети не є безпечними для ниток

Це дозволяє отримати доступ до декількох потоків одночасно

якщо ти хочеш зробити його Servlet як безпечний для теми., U може продовжити

Implement SingleThreadInterface(i) що є порожнім інтерфейсом немає

методи

або ми можемо перейти до методів синхронізації

ми можемо зробити весь метод обслуговування синхронізованим за допомогою синхронізованого

ключове слово перед методом

Приклад ::

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

або ми можемо поставити блок коду в блок синхронізації

Приклад ::

Synchronized(Object)

{

----Instructions-----

}

Я відчуваю, що синхронізований блок є кращим, ніж цілий метод

Синхронізовано

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.