Всем доброго и постепенно наступающего!

До окончания этого года остаётся не так уж и много времени, но мы ещё похоже успеем запустить два курса, одним из которых будет курс по Java EE. Так что держите первую часть заметок по Servlet 4.0

Новый крупный релиз API Servlet охватывает протокол HTTP/2 и прогнозирует потребности в ресурсах.

Долгожданное обновление для Java EE 8 включает в себя обновления существующих API: JAX-RS 2.1, Bean Validation 2.0, JavaServer Faces (JSF) 2.3, Contexts and Dependency Injection (CDI) 2.0, JSON with Padding (JSONP) 1.1 и Servlet 4.0, а также два новых API: JSON-Binding (JSON-B) и Java EE Security. Среди этих API, Servlet 4.0 представляет собой достаточно крупное обновление, первое с 2009 года.

Импульс, который вызвал этот большой релиз (а не точечное обновление), — это глобальное развертывание протокола HTTP/2 и множество новых возможностей, которые он приносит. Это обновление для HTTP является первым за почти 20 лет и устраняет многие недостатки HTTP 1.x. Новые возможности многочисленны (мультиплексирование запросов/ответов, сжатие заголовков, приоритизация потоков и push-сервер), но наиболее заметной функцией для пользователей Servlet API является Server Push, о котором я расскажу в этой статье.

Server Push — это не единственное примечательное дополнение к Servlet 4.0. Этот релиз также представляет усовершенствования в виде Servlet Mapping API, который поддерживает распознавание URL-отображений во время выполнения, за счет улучшений получения ссылочных путей. В этой статье обсуждаются эти функции, и как Server Push был интегрирован в API JavaServer Faces 2.3.


Server Push

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

Пропала необходимость разбивать ресурсы по доменам, чтобы обойти ограничения TCP-соединений браузера. Просто укажите ресурсы и позвольте серверу обрабатывать доставку. Это способ, который должен был существовать с самого начала.

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

Server Push в действии

Сервлеты — это базовая технология, лежащая в основе Java EE веб-приложений. Они обеспечивают возможности сервера, которые составляют основу многих фреймворков. JavaServer Faces (JSF) полагается на FacesServlet для управления жизненным циклом обработки запросов для веб-приложений, а страницы JSP переводятся в сервлеты по первому запросу клиента; поэтому неудивительно, что сервлеты — это естественное место для внедрения абстракции HTTP/2 Server Push.

Эта абстракция представлена как объект PushBuilder и создается путем вызова метода newPushBuilder() из экземпляра запроса HttpServletRequest, переданного всем переопределенным методам обработки запросов.

С помощью экземпляра PushBuilder вы можете начать проталкивать ресурсы, требуемые запрашиваемой веб-страницей. Ресурс устанавливается на экземпляр PushBuilder, передавая его местоположение методу path(). Ресурс доставляется клиенту вызовом метода push(). Его можно использовать повторно для отправки необходимого количества ресурсов.

В листинге 1 и 2 показаны простейшие примеры использования Server Push. В листинге 1 показан сервлет, который отвечает на запрос GET на URI /simplestexample:

Листинг 1

@WebServlet(“/simplestexample”)
public class SimplestExample extends HttpServlet { 
   @override 
   protected void doGet(HttpServletRequest request,
      HttpServletResponse response)
      throws ServletException, IOException {
      request.newPushBuilder()
         .path("images/coffee-cup.jpg")
         .push();
      getServletContext()
         .getRequestDispatcher("/coffee-cup.jsp") .forward(request, response); }}


В листинге 2 показана веб-страница JSP:

Листинг 2

<html>
<head>
   <title>Coffee-Cup</title>
</head> 
<body>
   <img src=’images/coffee-cup.jpg'>
</body> 
</html>


В листинге 1 есть сервлет под названием SimplestExample и JSP, на которую перенаправляется сервлет. Как вы можете видеть, на странице JSP требуется только один ресурс — изображение coffee-cup.jpg. Когда вызывается метод обработки запроса doGet(), он создает новый экземпляр PushBuilder, устанавливает местоположение изображения и вызывает метод push() для отправки ресурса клиенту.

Пока изображение направляется к клиенту, сервлет отправляет запрос в JSP, для которого требуется coffee-cup.jpg ресурс. К моменту, когда браузер получил обработанный HTML, он уже имеет изображение в своем кэше и может отображать страницу без необходимости делать ещё один запрос.

Чтобы увидеть действие сервера в действии, вы можете использовать инструменты разработчика, предоставляемые браузером Chrome от Google. Выберите «Дополнительные инструменты»> «Инструменты разработчика», а затем перейдите на вкладку «Сеть» и отправьте запрос на сервлет SimplestExample. Вы должны увидеть результат, аналогичный показанному на рисунке 1. Вы можете четко видеть, что используемый протокол — h2 (сокращение от «HTTP/2»), и изображение было инициировано с помощью Push. Это подтверждает, что Server Push использовался для удовлетворения запроса ресурса.


Рисунок 1: Запрос ресурсов, удовлетворенный Server Push

TLS необходимый для HTTP/2

На рисунке 1 вы могли заметить, что схема запроса — HTTPS. Это связано с тем, что все основные поставщики браузеров выбрали реализацию протокола HTTP/2 только через Transport Layer Security (TLS). Однако спецификация не гарантирует, что для успешного соединения HTTP/2 требуется безопасное соединение. Поставщики браузеров приняли это решение от нашего имени.

Необходимо соблюдать осторожность при использовании нового объекта PushBuilder. Вызов newPushBuilder() будет возвращать значение null, если соединение небезопасно, если клиент не поддерживает Server Push, или если клиент попросил отключить Server Push через параметр SETTINGS_ENABLE_PUSH раздела SETTINGS.

Если вы хотите попробовать этот пример для себя, вы можете скопировать код из репозитория GitHub.

Анатомия PushBuilder

Каждый новый экземпляр PushBuilder, созданный при вызове newPushBuilder(), основан на текущем экземпляре HttpServletRequest. Он инициируется с помощью метода HTTP GET, и все заголовки удаляются, за исключением conditional, range, expect, authorization и referrer заголовков.

PushBuilder реализует шаблон Builder, в котором цепь вызовов методов используются для изменения свойств экземпляра перед вызовом метода push(). Путь к ресурсам — единственная конфигурация, необходимая перед отправкой. Действие push инициирует асинхронный неблокирующий запрос, и когда он возвращается, условные заголовки и заголовки путей очищаются при подготовке к повторному использованию экземпляра Builder.

Возможно, вам интересно, насколько полезно использовать PushBuilder таким образом в реальном приложении, и это ризонный вопрос. Пример был немного искусственным и предназначен только для демонстрации. Более вероятным сценарием является то, что ваше приложение будет использовать JSF или JSP. Итак, давайте посмотрим на интеграцию JSF и то, как вы можете использовать функцию Server Push с JSP.

Особенно приветствуется бесшовная интеграция Server Push в API JSF, что позволяет использовать возможности HTTP/2, повышающие производительность, без каких-либо изменений кода.

THE END

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

Комментарии (3)


  1. vlanko
    21.12.2017 21:30

    Кроме GlassFish 5.0 где-то реализовано?
    Spring 5.


    1. MaxRokatansky Автор
      22.12.2017 19:14

      Вы про Server Push же? Очевидно там, где есть поддержка Servlet API 4.0 — это штука будет работать. Например, Томкат, начиная с версии 9.0.0.M1, поддерживает данную фичу.


    1. MaxRokatansky Автор
      22.12.2017 21:07

      Пардон, не дописал про Спринг: Так же как и Спринг :)