Как работают сервлеты? Создание экземпляров, сеансы, общие переменные и многопоточность

1201

Предположим, у меня есть веб-сервер с множеством сервлетов. Для передачи информации между сервлетами я устанавливаю переменные сеанса и экземпляра.

Теперь, если 2 или более пользователей отправят запрос на этот сервер, что произойдет с переменными сеанса?
Все они будут общими для всех пользователей или будут разными для каждого пользователя?
Если они разные, то как сервер мог различать разных пользователей?

Еще один похожий вопрос: если есть nпользователи, обращающиеся к определенному сервлету, тогда этот сервлет создается только в первый раз, когда первый пользователь обращается к нему, или он создается для всех пользователей отдельно?
Другими словами, что происходит с переменными экземпляра?

1892

ServletContext

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

Если a Servletимеет значение <servlet><load-on-startup>или @WebServlet(loadOnStartup)больше 0, то его init()метод также вызывается во время запуска с новым ServletConfig. Эти сервлеты инициализируются в том же порядке, что и в этом значении ( 11-й, 22-й и т. Д.). Если же значение задано более одного сервлета, то каждый из этих сервлет загружаются в том же порядке , как они появляются в web.xml, web-fragment.xmlили @WebServletзагрузке классов. Если значение «load-on-startup» отсутствует, 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его именем и уникальным идентификатором сеанса в качестве его значения.

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

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

На стороне клиента файл cookie сеанса остается активным, пока работает экземпляр браузера. Итак, если клиент закрывает экземпляр браузера (все вкладки / окна), то сеанс удаляется на стороне клиента. В новом экземпляре браузера cookie, связанный с сеансом, не существует, поэтому он больше не будет отправляться. Это приводит к HttpSessionсозданию совершенно нового файла cookie с использованием совершенно нового файла cookie сеанса.

В двух словах

  • В ServletContextжизни так долго , как веб - приложение жизни. Он распределяется между всеми запросами во всех сеансах.
  • В HttpSessionжизни до тех пор , пока клиент взаимодействует с веб - приложение с тем же экземпляром браузера, и сеанс не истекло на стороне сервера. Он распределяется между всеми запросами в одном сеансе.
  • HttpServletRequestИ HttpServletResponseжить с момента сервлет получает запрос HTTP от клиента, пока полный ответ (веб - страницы) не поступило. Его больше нигде не делят.
  • Все Servlet, Filterи Listenerэкземпляры живут, пока живет веб-приложение. Они распределяются между всеми запросами во всех сеансах.
  • Все, attributeчто определено в ServletContext, HttpServletRequestи HttpSessionбудет жить, пока живет рассматриваемый объект. Сам объект представляет собой «область действия» в средах управления компонентами, таких как JSF, CDI, Spring и т. Д. Эти структуры хранят компоненты с заданной областью видимости как одну 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.
    } 
}

Смотрите также:

23
  • 26 год
    Итак, когда я каким-то образом могу узнать JSessionId, который отправляется клиенту, я могу украсть его сеанс?
    Toskan
    30 нояб.
  • 56
    @Toskan: это правильно. Это известно как взлом фиксации сеанса . Обратите внимание, что это не относится к JSP / сервлету. Все другие серверные языки, поддерживающие сеанс с помощью файлов cookie, также чувствительны, например, PHP с PHPSESSIDcookie, ASP.NET с ASP.NET_SessionIDcookie и т. Д. Вот почему переписывание URL, ;jsessionid=xxxкак это делают некоторые фреймворки JSP / Servlet MVC автоматически, не одобряется. Просто убедитесь, что идентификатор сеанса никогда не отображается в URL-адресе или другими способами на веб-страницах, чтобы не подозревавший конечный пользователь не подвергся атаке.
    BalusC
    30 нояб.
  • 11
    @Toskan: Также убедитесь, что ваше веб-приложение не чувствительно к XSS-атакам. Т.е. не отображать повторно любой управляемый пользователем ввод в неэкранированной форме. XSS открыла двери для способов сбора идентификаторов сеансов всех конечных пользователей. См. Также Какова общая концепция XSS?
    BalusC
    30 нояб.
  • 2
    @BalusC, извините за мою глупость. Это означает, что все пользователи обращаются к одному и тому же экземпляру thisIsNOTThreadSafe, верно? 26 авг.
  • 4
    @TwoThumbSticks 404 возвращается, когда отсутствует сам сервлет. 405 возвращается, когда сервлет присутствует, но желаемый метод doXxx () не реализован.
    BalusC
    20 фев '17 в 11:52
436

Сессии

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

Вкратце: веб-сервер выдает уникальный идентификатор каждому посетителю при его первом посещении. Посетитель должен вернуть этот идентификатор, чтобы его узнали в следующий раз. Этот идентификатор также позволяет серверу правильно отделить объекты, принадлежащие одному сеансу, от объектов другого.

Создание сервлета

Если нагрузки на старте является ложным :

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

Если нагрузки на старте является истинным :

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

Когда он находится в сервисном режиме и в канавке, тот же сервлет будет работать с запросами от всех других клиентов.

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

Почему не рекомендуется иметь по одному экземпляру для каждого клиента? Подумайте об этом: будете ли вы нанимать одного пиццаря на каждый заказанный вами заказ? Сделайте это, и вы окажетесь вне бизнеса в кратчайшие сроки.

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

3
  • 27
    Ваша фотография очень хороша для моего понимания. У меня один вопрос: что будет делать этот ресторан с пиццей, когда придет слишком много заказов на пиццу, просто подождите одного пиццы или наймите еще одного пиццы? Спасибо .
    zh18
    16 янв.
  • 6
    Он вернет сообщение с to many requests at this moment. try again later 10 апр '17 в 21:40
  • 4
    Сервлеты, в отличие от сотрудников службы доставки пиццы, могут одновременно выполнять более одной доставки. Им просто нужно уделить особое внимание тому, где они записывают адрес клиента, вкус пиццы ...
    bruno
    04 янв.
42

Сеанс в сервлетах Java такой же, как сеанс в других языках, таких как PHP. Это уникально для пользователя. Сервер может отслеживать это различными способами, такими как файлы cookie, перезапись URL-адреса и т. Д. В этой статье документа Java объясняется это в контексте сервлетов Java и указывается, что именно то, как поддерживается сеанс, является деталью реализации, оставленной разработчикам сервера. В спецификации только оговаривается, что он должен быть уникальным для пользователя при нескольких подключениях к серверу. Прочтите эту статью Oracle для получения дополнительной информации по обоим вашим вопросам.

Edit Существует отличный учебник здесь о том , как работать с сеансом внутри сервлет. А вот глава Sun о сервлетах Java, о том, что это такое и как их использовать. Между этими двумя статьями вы сможете ответить на все свои вопросы.

3
  • Это вызывает у меня еще один вопрос: поскольку существует только один контекст сервлета для всего приложения, и мы получаем доступ к переменным сеанса через этот контекст сервлета, так как же переменные сеанса могут быть уникальными для каждого пользователя? Спасибо..
    Ku Jon
    24 июня '10 в 1:27
  • 1
    как вы получаете доступ к сеансу из servletContext? Вы не имеете в виду servletContext.setAttribute (), не так ли?
    matt b
    24 июня '10 в 1:44
  • 5
    @KuJon Каждое веб-приложение имеет один ServletContextобъект. Этот объект имеет ноль, один или несколько объектов сеанса - коллекцию объектов сеанса. Каждый сеанс идентифицируется некоторой строкой идентификатора, как показано в карикатурах на другой ответ. Этот идентификатор отслеживается на клиенте с помощью файлов cookie или перезаписи URL. Каждый объект сеанса имеет свои собственные переменные. 22 янв.
33

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

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

Механизм сервлета создает объект интерфейса servletConfig, инкапсулируя в него указанную выше недостающую информацию. Механизм сервлета вызывает init () сервлета, передавая ссылки на объекты servletConfig в качестве аргумента. После полного выполнения init () сервлет готов обслуживать запрос клиента.

Q) Сколько раз за время жизни сервлета происходит создание и инициализация ??

A) только один раз (для каждого клиентского запроса создается новый поток) только один экземпляр сервлета обслуживает любое количество клиентских запросов, т.е. после обслуживания одного клиентского запроса сервер не умирает. Он ожидает других клиентских запросов, то есть того, какие ограничения CGI (для каждого клиентского запроса создается новый процесс) преодолевается с помощью сервлета (внутренний механизм сервлета создает поток).

В) Как работает концепция сеанса?

A) всякий раз, когда getSession () вызывается для объекта HttpServletRequest

Шаг 1 : объект запроса оценивается на предмет идентификатора входящего сеанса.

Шаг 2 : если идентификатор недоступен, создается новый объект HttpSession и создается соответствующий ему идентификатор сеанса (например, HashTable). Идентификатор сеанса сохраняется в объекте ответа httpservlet, а ссылка на объект HttpSession возвращается сервлету (doGet / doPost) .

Шаг 3 : если ID доступен, новый объект сеанса не создан, идентификатор сеанса выбирается из объекта запроса, поиск выполняется в коллекции сеансов с использованием идентификатора сеанса в качестве ключа.

После успешного поиска идентификатор сеанса сохраняется в HttpServletResponse, а ссылки на существующие объекты сеанса возвращаются в doGet () или doPost () UserDefineservlet.

Примечание:

1) когда управление переходит от кода сервлета к клиенту, не забывайте, что объект сеанса удерживается контейнером сервлета, то есть механизмом сервлета

2) многопоточность оставлена ​​разработчикам сервлетов для реализации, т. Е. Обрабатывать несколько запросов клиента, не беспокоясь о многопоточном коде

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

Сервлет создается при запуске приложения (оно развертывается в контейнере сервлета) или при первом обращении к нему (в зависимости от настройки загрузки при запуске) при создании экземпляра сервлета вызывается метод init () сервлета. затем сервлет (его единственный экземпляр) обрабатывает все запросы (его метод service () вызывается несколькими потоками). Вот почему не рекомендуется иметь в нем какую-либо синхронизацию, и вам следует избегать переменных экземпляра сервлета, когда приложение не развернуто (контейнер сервлета останавливается), вызывается метод destroy ().

20

Сессии - что сказал Крис Томпсон.

Создание экземпляра - сервлет создается, когда контейнер получает первый запрос, сопоставленный с сервлетом (если сервлет не настроен на загрузку при запуске с <load-on-startup>элементом внутри web.xml). Тот же экземпляр используется для обслуживания последующих запросов.

1
  • 3
    Верный. Дополнительная мысль: каждый запрос получает новый (или переработанный) поток для запуска на этом единственном экземпляре сервлета. Каждый сервлет имеет один экземпляр и, возможно, несколько потоков (если много одновременных запросов). 22 янв.
13

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

A servlet container may send concurrent requests through the service method of the servlet. To handle the requests, the Servlet Developer must make adequate provisions for concurrent processing with multiple threads in the service method.

Although it is not recommended, an alternative for the Developer is to implement the SingleThreadModel interface which requires the container to guarantee that there is only one request thread at a time in the service method. A servlet container may satisfy this requirement by serializing requests on a servlet, or by maintaining a pool of servlet instances. If the servlet is part of a Web application that has been marked as distributable, the container may maintain a pool of servlet instances in each JVM that the application is distributed across.

For servlets not implementing the SingleThreadModel interface, if the service method (or methods such as doGet or doPost which are dispatched to the service method of the HttpServlet abstract class) has been defined with the synchronized keyword, the servlet container cannot use the instance pool approach, but must serialize requests through it. It is strongly recommended that Developers not synchronize the service method (or methods dispatched to it) in these circumstances because of detrimental effects on performance

3
0

Нет. Сервлеты не являются потокобезопасными.

Это позволяет получить доступ к более чем одному потоку одновременно.

если вы хотите сделать сервлет потокобезопасным., вы можете пойти на

Implement SingleThreadInterface(i) это пустой интерфейс, нет

методы

или мы можем использовать методы синхронизации

мы можем сделать весь метод обслуживания синхронизированным, используя синхронизированный

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

Пример::

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

или мы можем поместить блок кода в блок Synchronized

Пример::

Synchronized(Object)

{

----Instructions-----

}

Я считаю, что синхронизированный блок лучше, чем создание всего метода

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

0

Как ясно из объяснений выше, реализуя SingleThreadModel , сервлет может быть обеспечен потокобезопасностью с помощью контейнера сервлета. Реализация контейнера может сделать это двумя способами:

1) Сериализация запросов (постановка в очередь) к одному экземпляру - это похоже на сервлет, НЕ реализующий SingleThreadModel, НО синхронизирующий методы service / doXXX; ИЛИ

2) Создание пула экземпляров - это лучший вариант и компромисс между усилием загрузки / инициализации / временем сервлета по сравнению с ограничивающими параметрами (память / время процессора) среды, в которой размещен сервлет.