Я наткнулся на часть на странице Head First JSP и Servlets: 241, где говорится, что мы должны избавиться от сеансов, как показано на этом рисунке:
Позже они представляют методы invalidate()
и setMaxInactiveInterval()
, которые используются для уменьшения количества устаревших сессий на нашем сервере. Прочитав это, я немного запутался.
Для начала я получаю объект сеанса внутри кода сервлета, используя HttpSession s = request.getSession()
, а затем выполняю некоторые операции. Знание того, что один запрос создаст один поток для этого сервлета, означает, что переменная s
будет иметь область действия только для этого данного потока. После завершения потока переменная s
перестанет существовать. Что также означает, что объект сеанса в куче не будет иметь активной ссылки из s
= сбор мусора.
Так что, если новых запросов нет, не должно быть никаких объектов сеанса, использующих мои ресурсы, верно? Так почему же книга говорит мне, что я должен избавиться от них? Разве сборщик мусора не должен делать свою работу в одиночку?
Может ли кто-нибудь поправить меня, сказав, что я написал неправильно? И действительно ли объекты сеанса хранятся внутри кучи? Поскольку я не могу придумать никакого другого места, где они могли бы быть.
Этот вопрос и принятый на него ответ дают хорошее представление о том, для чего предназначен механизм сеанса HTTP.
Если вы хотите сохранять состояние между запросами, то это состояние нужно где-то хранить. Я считаю, что большинство серверов приложений по умолчанию будут использовать для этого кучу. Вы можете представить себе, что это карта с идентификатором сеанса пользователя в качестве ключа и объектом, содержащим сохраненные данные в качестве его значения.
Эта карта будет расти вечно, если вы не предпримете никаких действий по удалению из нее объектов, либо аннулировав объект сеанса, когда пользователь выйдет из системы, либо установив какое-то ограничение бездействия, которое позволяет самому серверу приложений очищать старые записи через заданный период времени. .
Данные сеанса хранятся на сервере. Файл cookie пользователя содержит только ключевое значение, позволяющее серверу искать значения, которые были сохранены в сеансе. Когда вы создаете сеанс на сервере, сервер передает идентификатор сеанса обратно клиенту в файле cookie. Клиент передает этот файл cookie обратно при всех последующих запросах, позволяя серверу получить идентификатор, а затем найти все данные, связанные с сеансом.
Спасибо! Срок действия куки-файлов по умолчанию равен времени, пока открыт браузер. Я знаю, что могу увеличить срок жизни файла cookie, но могу ли я продлить срок службы файла cookie, содержащего идентификатор сеанса? Я знаю, что это может нарушить правила (поскольку сессия существует, пока открыт браузер), но могу ли я сделать это теоретически?
Я не думаю, что есть что-то особенное в файле cookie, который содержит идентификатор сеанса, поэтому вы, вероятно, могли бы расширить его, если хотите.
Здесь довольно много вещей, которые нужно развернуть, так что давайте рассмотрим их одну за другой.
HTTP — это протокол без сохранения состояния. Это означает, что для сервера каждый HTTP-запрос рассматривается как независимый от других HTTP-запросов. Таким образом, если вы делаете несколько запросов к одному и тому же серверу, серверу все равно, исходят ли они от одного и того же клиента или нет: получен запрос и сгенерирован ответ, получен другой запрос и сгенерирован еще один ответ, и так далее. на.
Но бывают ситуации, когда вам нужно идентифицировать кучу запросов как исходящих от одного и того же пользователя, как более длительное взаимодействие с сервером, а не только отдельные запросы. И вот здесь на помощь приходят сеансы и файлы cookie.
Сеанс идентифицирует несколько взаимодействий одного и того же пользователя с сервером и позволяет вам поддерживать личность пользователя и некоторые полезные данные, время жизни которых может охватывать все запросы. Это означает, что сеанс с состоянием, а не без состояния.
Сеанс — это, по сути, объект, который сервер хранит в памяти, который действует как контейнер для любых данных, которые вы хотите сохранить между запросами. Этот объект также может быть сохранен на диске или внутри базы данных (например, когда вы перезапускаете сервер и не хотите терять активные сеансы), но для простоты просто считайте его объектом в памяти. И да, он хранится внутри HEAP.
Таким образом, на сервере вы можете создать объект сеанса, если вашему приложению необходимо сохранять состояние между запросами. Но как отличить запросы, принадлежащие одному сеансу, от других запросов, не принадлежащих этому сеансу? Ответ - печенье.
Когда пользователь делает свой первый запрос, сервер может создать сеанс и вернуть вам SESSION_ID, который будет добавлен к ответу. Когда пользователь затем делает другой запрос, SESSION_ID отправляется обратно на сервер, и теперь этот запрос идентифицируется как часть более крупного взаимодействия, часть сеанса. Какой сеанс? Это идентифицируется с SESSION_ID. Таким образом, сеанс — это объект, хранящийся на сервере, и любые запросы, которые являются частью этого взаимодействия сеанса, должны быть идентифицированы с помощью SESSION_ID.
Поскольку объект сеанса — это объект Java, который находится в куче, он может быть удален сборщиком мусора. Однако это не так просто.
Сравните, например, следующие фрагменты кода. Этот:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ...
Object s = new Object();
// ...
}
с этим:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ...
HttpSession s = request.getSession();
// ...
}
В первом примере вы создаете объект, который хранится в куче. Как только метод doGet
завершается, этот объект подходит для сборки мусора, потому что больше нет ссылок на объект, кроме s
, который выходит из области видимости, когда метод возвращается.
Ключевая часть здесь - «больше никаких ссылок». Объект подлежит сбору мусора, когда доступ к объекту больше невозможен из любой из действующих ссылок, существующих внутри JVM. Когда метод doGet
завершается, s
исчезает, поэтому ничто не указывает на созданный вами объект. С HttpSession
все иначе.
Во втором фрагменте кода вы не создаете объект сеанса, вы просите сервер «дать вам» объект сеанса. Представьте себе карту, сохраненную сервером, которая содержит объекты сеанса в качестве значений, а идентификаторы SESSION_ID являются ключами для доступа к ним. Когда вы просите сервер предоставить вам сеанс с HttpSession s = request.getSession()
, он просматривает файл cookie SESSION_ID из запроса, чтобы найти сеанс, связанный с этим запросом, и дает вам ссылку на объект сеанса. Теперь у вас есть две ссылки на объект сеанса, один хранится на сервере в этой карте сеансов, а другой хранится в s
. Когда метод doGet
завершается, ссылка s
исчезает, но сервер все еще хранит ссылку на объект сеанса (поскольку он нужен ему для дальнейших запросов, которые могут поступать как часть более крупного взаимодействия). В этом случае объект сеанса НЕ подходит для сборки мусора, потому что он доступен по активной ссылке в JVM, той, которая хранится на сервере.
Итак, если вы не избавитесь от сеансов, сервер не сможет узнать, полезен этот сеанс или бесполезен, потому что он не знает, будет ли позже сделан другой запрос, чтобы запросить его или нет. Таким образом, объекты сеанса остаются на сервере навсегда. Поскольку сервер может работать месяцами или годами без перезапуска или отключения, объекты сеанса могут накапливаться и потреблять всю память. Сборщик мусора не удалит их, потому что на них есть ссылки на сервере. В конечном итоге вы получите ошибку OutOfMemory, и ваш сервер выйдет из строя.
Конечно, вы не хотите, чтобы ваш сервер рухнул. Поэтому вам нужно аннулировать сеансы и сказать серверу: «Эй, мне больше не нужен этот объект сеанса. Вы можете избавиться от него». В этом случае сервер удаляет его со своей карты, и теперь без каких-либо активных ссылок он может быть удален сборщиком мусора.
Но так как все это взаимодействие происходит по сети с HTTP, как упоминается в примере в книге, браузер может рухнуть, компьютер пользователя может рухнуть, пользователь может просто уйти. Таким образом, у вас может не быть возможности аннулировать сеанс и сказать серверу, что он может избавиться от него, поэтому он останется там навсегда.
И вот тут-то и появляются тайм-ауты сеанса. Когда вы создаете свое приложение, вы также настраиваете тайм-аут сеанса, чтобы сообщить серверу: «Эй, если в этом сеансе нет активности в течение X минут, вы можете избавиться от этого». Итак, теперь, если клиент просто уходит, не аннулируя сеанс, сервер может иметь отказоустойчивый механизм для избавления от просроченных сеансов, чтобы они не оставались навсегда в памяти.
Мужик, спасибо тебе большое, ты все рассказал! Короче говоря: ссылочная переменная s
будет ссылаться на тот же объект, что и объект сеанса внутри памяти? Итак, две ссылки на один объект, верно? Поэтому, когда поток завершается, остается только одна ссылка (одна из карты). Я понял? :)
Да. Ваша ссылка s
пропала, но с сервера осталась. Только когда сеанс станет недействительным или истечет время ожидания, сервер удалит свою ссылку, и тогда у вас будут нулевые ссылки, и объект сеанса может быть удален сборщиком мусора.
Понял :) Этот ответ нуждается в большем количестве голосов, он превосходен. Даже в книге подробно это не описано. И еще один вопрос: могу ли я когда-нибудь увеличить срок жизни файла cookie, содержащего SESSION_ID? Я знаю, что это нарушит правила (файл cookie сеанса удаляется при закрытии браузера), но могу ли я это сделать? Я заметил, что они умно разработали его в сервлетах - метод getSession()
, который технически возвращает сеанс, а не его файл cookie. Так что этот API фактически мешает мне это сделать. Верна ли моя догадка?
Если для файла cookie установлена дата истечения срока действия, браузер должен хранить его до истечения срока действия, а не до тех пор, пока вы не закроете браузер. Найдите «Expires» в строке cookie в ответе HTTP или в консоли отладки браузера. Что касается установки возраста файла cookie, мне не совсем понятно, если это то, что вы ищете, но посмотрите на <cookie-config><max-age>
в web.xml. См., например, этот пост: stackoverflow.com/questions/35105410/…
Могу ли я тогда сказать, что сеанс хранится на стороне сервера, а также в куки-файлах пользователя? Поскольку определение того, что он находится на сервере, сбивает с толку ... серверу необходимо проверить идентификатор и сравнить его с тем, что «у него есть». Я близок к этому?