Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Асинхронная обработка

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

Существует два распространённых сценария, в которых поток, связанный с запросом, может бездействовать.

  • Поток должен дождаться, когда ресурс станет доступным, или обработать данные, прежде чем создавать ответ. Например, приложению может потребоваться запросить базу данных или получить доступ к данным из удалённого веб-сервиса, прежде чем генерировать ответ.

  • Поток должен ждать события, прежде чем генерировать ответ. Например, приложению может потребоваться дождаться сообщения JMS, новой информации от другого клиента или новых данных, доступных в очереди, прежде чем генерировать ответ.

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

Асинхронная обработка в сервлетах

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

Чтобы включить асинхронную обработку в сервлете, установите для параметра asyncSupported значение true в аннотации @WebServlet следующим образом:

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet { ... }

Класс javax.servlet.AsyncContext предоставляет функциональные возможности, необходимые для выполнения асинхронной обработки внутри сервисных методов. Чтобы получить объект AsyncContext, вызовите метод startAsync() для объекта запроса вашего сервисного метода. Например:

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
   ...
   AsyncContext acontext = req.startAsync();
   ...
}

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

Таблица 18-3 описывает основные функциональные возможности, предоставляемые классом AsyncContext.

Таблица 18-3. Функциональность, предоставляемая классом AsyncContext

Сигнатура метода

Описание

void start(Runnable run)

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

Вы предоставляете код для операции блокировки как класс, который реализует интерфейс Runnable. Вы можете предоставить этот класс как внутренний класс при вызове метода start или использовать другой механизм для передачи объекта AsyncContext в ваш класс.

ServletRequest getRequest()

Возвращает запрос, использованный для инициализации этого асинхронного контекста. В приведённом выше примере запрос такой же, как и в сервисном методе.

Вы можете использовать этот метод внутри асинхронного контекста для получения параметров из запроса.

ServletResponse getResponse()

Возвращает ответ, использованный для инициализации этого асинхронного контекста. В приведённом выше примере ответ такой же, как и в сервисном методе.

Вы можете использовать этот метод внутри асинхронного контекста для записи в ответ с результатами операции блокировки.

void complete()

Завершает асинхронную операцию и закрывает ответ, связанный с этим асинхронным контекстом.

Вызывайте этот метод после записи в объект ответа в асинхронном контексте.

void dispatch(String path)

Отправляет объекты запроса и ответа по указанному пути.

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

В ожидании ресурса

В этом разделе показано, как использовать функциональные возможности, предоставляемые классом AsyncContext для следующего варианта использования:

  1. Сервлет получает параметр из запроса GET.

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

  3. Сервлет генерирует ответ, используя результат из ресурса.

Следующий код показывает базовый сервлет, который не использует асинхронную обработку:

@WebServlet(urlPatterns={"/syncservlet"})
public class SyncServlet extends HttpServlet {
   private MyRemoteResource resource;
   @Override
   public void init(ServletConfig config) {
      resource = MyRemoteResource.create("config1=x,config2=y");
   }

   @Override
   public void doGet(HttpServletRequest request,
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      String param = request.getParameter("param");
      String result = resource.process(param);
      /* ... запись в ответ... */
   }
}

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

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
   /* ... Same variables and init method as in SyncServlet ... */

   @Override
   public void doGet(HttpServletRequest request,
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      final AsyncContext acontext = request.startAsync();
      acontext.start(new Runnable() {
         public void run() {
            String param = acontext.getRequest().getParameter("param");
            String result = resource.process(param);
            HttpServletResponse response = acontext.getResponse();
            /* ... запись в ответ... */
            acontext.complete();
   }
}

AsyncServlet добавляет asyncSupported=true к аннотации @WebServlet. Остальные различия находятся внутри сервисного метода.

  • request.startAsync() заставляет запрос обрабатываться асинхронно. Ответ не отправляется клиенту в конце сервисного метода.

  • acontext.start(new Runnable() {…}) получает новый поток из контейнера.

  • Код внутри метода run() внутреннего класса выполняется в новом потоке. Внутренний класс имеет доступ к асинхронному контексту для чтения параметров из запроса и записи в ответ. Вызов метода complete() асинхронного контекста фиксирует ответ и отправляет его клиенту.

Служебный метод AsyncServlet немедленно возвращается, и запрос обрабатывается в асинхронном контексте.


Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)