Как работают сервлеты?Инстанциация, сессии, общие переменные и многопоточность - PullRequest
1075 голосов
/ 24 июня 2010

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

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

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

Ответы [ 8 ]

1742 голосов
/ 24 июня 2010

ServletContext

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

Если Servlet имеет значение <servlet><load-on-startup> или @WebServlet(loadOnStartup) больше 0, то его метод init() также вызывается во время запуска с новым ServletConfig. Эти сервлеты инициализируются в том же порядке, который указан в этом значении (1 - 1-й, 2 - 2-й и т. Д.). Если одно и то же значение указано для нескольких сервлетов, каждый из этих сервлетов загружается в том же порядке, в котором они отображаются в классовой загрузке 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 действителен (т. Е. Уникальный идентификатор должен указывать на не истекший сеанс, а домен и путь указаны правильно).Используя встроенный в ваш браузер монитор трафика HTTP, вы можете убедиться, что файл cookie действителен (нажмите F12 в Chrome / Firefox 23+ / IE9 + и откройте вкладку Сеть / Сеть ).Контейнер сервлета будет проверять заголовок 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, будет жить какпока объект живет.Сам объект представляет «область» в инфраструктурах управления bean-компонентами, таких как JSF, CDI, Spring и т. Д. Эти структуры хранят свои bean-объекты области видимости как attribute своей ближайшей соответствующей области.

Thread Safety

Тем не менее, ваша главная проблема, возможно, безопасность потоков .Теперь вы должны знать, что сервлеты и фильтры являются общими для всех запросов.Это хорошая вещь в 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.
    } 
}

См. Также:

419 голосов
/ 06 июля 2013

сеансов

enter image description here enter image description here

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

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

Если загрузка при запуске равна false:

enter image description here enter image description here

Если загрузка при запуске равна true :

enter image description here enter image description here

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

enter image description here

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

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

42 голосов
/ 24 июня 2010

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

Редактировать Здесь есть отличное руководство по работе с сессиями внутри сервлетов. И здесь - это глава от Sun о Java-сервлетах, о том, что они из себя представляют и как их использовать. Между этими двумя статьями вы должны быть в состоянии ответить на все ваши вопросы.

33 голосов
/ 22 февраля 2012

Когда контейнер сервлета (например, 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) когда управление уходит от кода сервлета к клиенту, не забывайте, что объект сеанса удерживается контейнером сервлета, т. Е. Механизмом сервлета.множественные запросы клиента не беспокоят многопоточный код

Inshort form:

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

20 голосов
/ 24 июня 2010

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

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

13 голосов
/ 05 марта 2014

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

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

Хотя это и не рекомендуется, альтернативой для разработчика является реализовать интерфейс SingleThreadModel, который требует контейнера чтобы гарантировать, что за один раз в метод обслуживания. Контейнер сервлета может удовлетворить это требование сериализация запросов на сервлете или поддержание пула сервлетов экземпляров. Если сервлет является частью веб-приложения, которое было помеченный как распространяемый, контейнер может поддерживать пул сервлетов экземпляры в каждой JVM, между которыми распределено приложение.

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

0 голосов
/ 06 февраля 2019

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

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

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

0 голосов
/ 14 апреля 2018

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

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

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

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

Методы

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

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

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

Пример ::

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

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

Пример ::

Synchronized(Object)

{

----Instructions-----

}

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

Синхронное

...