Как работают сервлеты? Создание, сеансы, общие переменные и многопоточность
Предположим, у меня есть вебсервер, который содержит множество сервлетов. Для передачи информации между этими сервлетами я устанавливаю переменные сеанса и экземпляра.
Теперь, если 2 или более пользователей отправляют запрос этому серверу, что происходит с переменными сеанса? Будут ли они все распространены для всех пользователей или они будут отличаться для каждого пользователя. Если они разные, то как сервер мог различать разных пользователей?
Еще один подобный вопрос: если есть n
пользователи, обращающиеся к определенному сервлету, то этот сервлет получает экземпляр только в первый раз, когда первый пользователь обратился к нему или он получает экземпляр для всех пользователей отдельно? Другими словами, что происходит с переменными экземпляра?
Ответы
Ответ 1
ServletContext
Когда контейнер сервлета (например, Apache Tomcat) запускается, он развертывает и загружает все свои веб-приложения. Когда веб-приложение загружено, контейнер сервлета создает ServletContext
один раз и сохраняет его в памяти сервера. Файл web.xml
веб-приложения анализируется, и каждый найденный <servlet>
, <filter>
и <listener>
(или каждый класс, аннотированный @WebServlet
, @WebFilter
и @WebListener
соответственно) @WebListener
один раз и сохраняется в памяти сервера как Что ж. Для каждого экземпляра фильтра его метод init()
вызывается с новым FilterConfig
.
Когда Servlet
имеет <servlet><load-on-startup>
или @WebServlet(loadOnStartup)
значение больше 0
, то его init()
метод также вызывается при запуске с новым ServletConfig
. Эти сервлеты инициализируются в том же порядке, который указан в этом значении (1
- 1, 2
- 2 и т.д.). Если одно и то же значение указано для нескольких сервлетов, то каждый из этих сервлетов загружается в том же порядке, в котором они отображаются в web.xml
или @WebServlet
. Если значение "load-on-startup" отсутствует, метод init()
будет вызываться всякий раз, когда HTTP-запрос попадает в этот сервлет в первый раз.
Когда контейнер сервлета завершит все описанные выше шаги инициализации, будет вызван ServletContextListener#contextInitialized()
.
Когда контейнер сервлета завершает работу, он выгружает все веб-приложения, вызывает метод destroy()
всех своих инициализированных сервлетов и фильтров, и все экземпляры ServletContext
, Servlet
, Filter
и Listener
удаляются. Наконец, ServletContextListener#contextDestroyed()
будет вызван.
HttpServletRequest и HttpServletResponse
Контейнер сервлета подключен к веб-серверу, который прослушивает HTTP-запросы на определенный номер порта (порт 8080 обычно используется во время разработки, а порт 80 - в работе). Когда клиент (например, пользователь с помощью веб - браузера, или программно с HttpServletRequest
HttpServletResponse
Filter
Servlet
помощью URLConnection
) посылает запрос HTTP, контейнер сервлета создает новые HttpServletRequest
и HttpServletResponse
объектов и передает их через какой - либо определенный Filter
в цепи и, в конечном счете, к Servlet
экземпляра.
В случае фильтров вызывается метод doFilter()
. Когда код контейнера сервлета вызывает chain.doFilter(request, response)
, запрос и ответ 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
заголовка, пока печенье действительна (т.е. уникальный идентификатор должен относиться к не истекшему сеансу, а домен и путь указаны правильно). Используя встроенный в браузер монитор трафика HTTP, вы можете проверить, является ли файл cookie действительным (нажмите F12 в Chrome/Firefox 23+/IE9+ и откройте вкладку Сеть/Сеть). Контейнер сервлета будет проверять заголовок Cookie
каждого входящего HTTP-запроса на наличие cookie с именем JSESSIONID
и использовать его значение (идентификатор сеанса), чтобы получить связанный HttpSession
из памяти сервера.
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
своей ближайшей соответствующей области.
Поток безопасности
Тем не менее, ваша главная проблема, возможно, безопасность потоков. Теперь вы должны знать, что сервлеты и фильтры являются общими для всех запросов. Что приятно в 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.
}
}
Смотрите также:
Ответ 2
Сессия
Короче: веб-сервер выдает уникальный идентификатор каждому посетителю при первом посещении. Посетитель должен вернуть этот идентификатор, чтобы его узнали в следующий раз. Этот идентификатор также позволяет серверу правильно изолировать объекты, принадлежащие одному сеансу, к другому.
Мгновенное действие сервлета
Если load-on-startup false:
Если load-on-startup true:
Как только он в сервисном режиме и на канавке, тот же сервлет будет работать с запросами от всех других клиентов.
Почему не рекомендуется иметь один экземпляр для каждого клиента? Подумайте об этом: возьмете ли вы одного парня-пиццу за каждый приказ? Сделайте это, и вы не будете в бизнесе в кратчайшие сроки.
Он поставляется с небольшим риском. Помните: этот единственный парень хранит всю информацию о заказе в кармане, поэтому, если вы не будете осторожны в безопасности потоков на сервлетах, он может оказаться давая неправильный порядок определенному клиенту.
Ответ 3
Сессия в сервлетах Java совпадает с сеансом на других языках, таких как PHP. Он уникален для пользователя. Сервер может отслеживать его различными способами, такими как файлы cookie, переписывание URL и т.д. Эта статья Java doc объясняет это в контексте сервлетов Java и указывает, что именно то, как поддерживается сеанс, представляет собой деталь реализации, оставленную разработчикам сервера. В спецификации указывается, что она должна поддерживаться как уникальная для пользователя в нескольких соединениях с сервером. Ознакомьтесь с этой статьей из Oracle для получения дополнительной информации обо всех ваших вопросах.
Изменить Здесь есть отличный учебник о том, как работать с сеансом внутри сервлетов. И здесь - это глава от Sun о сервлетах Java, каковы они и как их использовать. Между этими двумя статьями вы сможете ответить на все ваши вопросы.
Ответ 4
Когда контейнер сервлета (например, Apache Tomcat) запускается, он будет читать из файла web.xml (только один на приложение), если что-то пойдет не так или будет отображаться ошибка на консоли на стороне контейнера, в противном случае он развернет и загрузит весь веб приложения с помощью web.xml (так называемый дескриптор развертывания).
На этапе создания экземпляра сервлета экземпляр сервлета готов, но он не может обслуживать клиентский запрос, поскольку отсутствует две части информации:
1: контекстная информация
2: информация о начальной конфигурации
Механизм сервлета создает интерфейсный объект servletConfig, инкапсулируя в него недостающую информацию, описанную выше. Механизм сервлета вызывает init() сервлета, предоставляя ссылки на объект servletConfig в качестве аргумента. Как только init() полностью выполнен, сервлет готов обслуживать запрос клиента.
Q) Сколько раз в жизни сервлета происходит инициализация и инициализация??
A) только один раз (для каждого клиентского запроса создается новый поток) только один экземпляр сервлета обслуживает любое количество клиентских запросов, т.е. После обслуживания один клиентский сервер запросов не умирает. Он ожидает других клиентских запросов, т.е. Какой CGI (для каждого клиентского запроса создается новый процесс) ограничение преодолевается с помощью сервлета (внутренний механизм сервлета создает поток).
Q) Как работает концепция сессии?
А) всякий раз, когда getSession() вызывается для объекта HttpServletRequest
Шаг 1: объект запроса оценивается для входящего идентификатора сеанса.
Шаг 2: если идентификатор недоступен, создается новый объект HttpSession и генерируется соответствующий ему идентификатор сеанса (т.е. HashTable). Идентификатор сеанса сохраняется в объекте ответа httpservlet, а ссылка на объект HttpSession возвращается сервлету (doGet/doPost).,
Шаг 3: если идентификатор доступен, новый объект сеанса не создан. Идентификатор сеанса выбирается из запроса. Поиск объекта производится в коллекции сеансов с использованием идентификатора сеанса в качестве ключа.
После успешного поиска идентификатор сеанса сохраняется в HttpServletResponse, а ссылки на существующие объекты сеанса возвращаются в doGet() или doPost() UserDefineservlet.
Замечания:
1) когда управление уходит от кода сервлета к клиенту, не забывайте, что объект сеанса удерживается контейнером сервлета, т.е. Механизмом сервлета
2) многопоточность оставлена разработчикам сервлетов для реализации, т.е. Обрабатывать множественные запросы клиента, не беспокоясь о многопоточном коде
Краткая форма:
Сервлет создается при запуске приложения (оно развертывается в контейнере сервлета) или при первом обращении к нему (в зависимости от настройки загрузки при запуске), когда создается сервлет, вызывается метод init() сервлета затем сервлет (его единственный экземпляр) обрабатывает все запросы (его метод service() вызывается несколькими потоками). Поэтому не рекомендуется использовать синхронизацию, и вам следует избегать переменных экземпляра сервлета, когда приложение не развернуто (контейнер сервлета останавливается), вызывается метод destroy().
Ответ 5
Сеансы - то, что сказал Крис Томпсон.
Instantiation - создается сервлет, когда контейнер получает первый запрос, сопоставленный сервлету (если сервлет не настроен на загрузку при запуске с элементом <load-on-startup>
в web.xml
). Один и тот же экземпляр используется для последующих запросов.
Ответ 6
Спецификация сервлета JSR-315 четко определяет поведение веб-контейнера в методах службы (и doGet, doPost, doPut и т.д.) (2.3.3.1 Проблемы многопоточности, Страница 9):
Контейнер сервлета может отправлять параллельные запросы через службу метод сервлета. Чтобы обрабатывать запросы, Servlet Developer должны сделать адекватные условия для параллельной обработки с несколькими потоков в сервисном методе.
Хотя это не рекомендуется, альтернативой для разработчика является реализовать интерфейс SingleThreadModel, для которого требуется контейнер чтобы гарантировать, что есть только один поток запросов за раз в метод обслуживания. Контейнер сервлетов может удовлетворять этому требованию сериализация запросов на сервлет или поддержание пула сервлета экземпляров. Если сервлет является частью веб-приложения, которое было помеченный как распространяемый, контейнер может содержать пул сервлетов экземпляры в каждой JVM, что приложение распределено между ними.
Для сервлетов, не реализующих интерфейс SingleThreadModel, если (или методы, такие как doGet или doPost, которые отправлен на сервисный метод абстрактного класса HttpServlet) было определено с помощью синхронизированного ключевого слова, контейнера сервлета не может использовать подход пула экземпляров, но должен выполнять сериализацию запросов через это. Настоятельно рекомендуется, чтобы разработчики не синхронизировали метод обслуживания (или методы, отправленные ему) в этих обстоятельства из-за пагубных воздействий на производительность
Ответ 7
Как ясно из приведенных выше объяснений, посредством реализации SingleThreadModel сервлет может гарантировать безопасность потока с помощью контейнера сервлета. Реализация контейнера может сделать это двумя способами:
1) Сериализация запросов (постановка в очередь) для одного экземпляра - это похоже на сервлет, НЕ реализующий SingleThreadModel, НО синхронизирующий методы service/doXXX; ИЛИ ЖЕ
2) Создание пула экземпляров - что является лучшим вариантом и компромисс между загрузкой/инициализацией/временем сервлета по сравнению с ограничительными параметрами (памятью/временем ЦП) среды, в которой размещен сервлет.
Ответ 8
Нет. Сервлеты не безопасны
Это позволяет получить доступ к более чем одному потоку одновременно
если вы хотите сделать его Servlet как потокобезопасным., вы можете пойти на
Implement SingleThreadInterface(i)
который является пустым интерфейсом там нет
методы
или мы можем пойти на методы синхронизации
мы можем сделать весь метод обслуживания как синхронизированный, используя синхронизированный
Ключевое слово перед методом
Пример::
public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException
или мы можем поставить блок кода в синхронизированный блок
Пример::
Synchronized(Object)
{
----Instructions-----
}
Я чувствую, что синхронизированный блок лучше, чем весь метод
синхронизированный