Статьи

Продолжение, чтобы продолжить

Jetty-6 Continuations представила концепцию асинхронных сервлетов для обеспечения масштабируемости и качества обслуживания приложений Web 2.0, таких как чат, совместное редактирование, публикация цен, а также мощных основанных на HTTP средах , таких как cometd , apache camel , openfire XMPP и flex BlazeDS.

С появлением аналогичных асинхронных функций в Servlet-3.0 некоторые предположили, что API Continuation будет устаревшим. Вместо этого API продолжения был обновлен, чтобы обеспечить упрощенную переносимость, выполняемую асинхронно на любом контейнере сервлета 3.0, а также на Jetty (6,7 и 8). Продолжения будут работать синхронно (блокироваться) на любом контейнере сервлета 2.5. Таким образом, программирование в API Continuations позволяет вашему приложению достичь асинхронности сегодня, не дожидаясь выпуска стабильных контейнеров 3.0 (и не требуя обновления всей вашей связанной инфраструктуры).

Улучшения продолжения

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

Кроме того, у старого API продолжения было продолжение ожидания, которое будет работать на серверах без джеттинга. Однако поведение этого ожидающего продолжения немного отличалось от нормального продолжения, поэтому код должен быть тщательно написан, чтобы работать для обоих.

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

Продолжения и сервлет 3.0

Асинхронный API сервлета 3.0 представил некоторые дополнительные асинхронные функции, не поддерживаемые продолжениями Jetty 6, включая:

  • Возможность выполнить асинхронный запрос без отправки
  • Поддержка упакованных запросов и ответов.
  • Слушатели асинхронных событий
  • Отправка асинхронных запросов в определенные контексты и / или ресурсы

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

  • Завершение продолжения без возобновления.
  • Поддержка упаковщиков ответов.
  • Дополнительные слушатели для асинхронных событий.

 

Использование API продолжения

Новый API-интерфейс продолжения доступен в Jetty-7 и вряд ли изменится в будущих выпусках. Также библиотека продолжения предназначена для развертывания в WEB-INF / lib и является переносимой. Таким образом, баночка продолжения пристани-7 будет работать асинхронно при развертывании в пристани-шестой, пристани-7, пристани-8 или любом контейнере с сервлетом 3.0.

Получение продолжения


Класс
фабрики
ContinuationSupport можно использовать для получения экземпляра продолжения, связанного с запросом:

    Continuation continuation = ContinuationSupport.getContinuation(request);

Приостановка запроса


Приостановить запрос, метод suspend вызывается на продолжение:

  void doGet(HttpServletRequest request, HttpServletResponse response)
{
...
continuation.suspend();
...
}

После вызова этого метода жизненный цикл запроса будет расширен за пределы возврата в контейнер из вызовов метода Servlet.service (…) и вызовов Filter.doFilter (…). После того, как эти методы отправки вернутся к, поскольку приостановленный запрос не будет принят, и ответ не будет отправлен HTTP-клиенту.

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

Запрос будет приостановлен до тех пор, пока не будет вызвано continueation.resume () или continueation.complete (). Если ни один из них не вызывается, то продолжение будет иметь тайм-аут после периода по умолчанию или времени, установленного перед приостановкой, путем вызова continueation.setTimeout (long). Если слушатели тайм-аута не возобновляют или не завершают продолжение, то продолжение возобновляется с продолжением. IsExpired () true. Существует вариант приостановки для использования с оболочками запросов и полным жизненным циклом (см. Ниже):

    continuation.suspend(response);

Приостановка аналогична методу request.startAsync () сервлета 3.0. В отличие от продолжений Jetty-6, исключение не генерируется приостановкой, и метод должен возвращаться нормально. Это позволяет регистрировать продолжение после приостановки и устраняет необходимость в мьютексе. Если желательно исключение (для обхода кода, который не знает о продолжениях и может попытаться зафиксировать ответ), то может быть вызвано continueation.undispatch () для выхода из текущего потока из текущей диспетчеризации путем выброса
ContinuationThrowable .

Возобновление запроса


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

  void myAsyncCallback(Object results)
{
continuation.setAttribute("results",results);
continuation.resume();
}

После продолжения продолжения запрос повторно отправляется в контейнер сервлета, почти как если бы запрос был получен снова. Однако во время повторной отправки метод continueation.isInitial () возвращает значение false, и все атрибуты, установленные асинхронным обработчиком, доступны.

Продолжение резюме аналогично Servlet 3.0 AsyncContext.dispatch ().

Завершение запроса


В качестве альтернативы выполнению запроса асинхронный обработчик может написать сам ответ.
После написания ответа обработчик должен указать, что обработка запроса завершена, вызвав метод complete:

  void myAsyncCallback(Object results)
{
writeResults(continuation.getServletResponse(),results);
continuation.complete();
}

После вызова завершения контейнер планирует ответ, который будет зафиксирован и очищен.

Продолжение резюме аналогично Servlet 3.0 AsyncContext.complete ().

Слушатели продолжения


Приложение может отслеживать состояние продолжения, используя
ContinuationListener :

  void doGet(HttpServletRequest request, HttpServletResponse response)
{
...

Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.addContinuationListener(new ContinuationListener()
{
public void onTimeout(Continuation continuation) { ... }
public void onComplete(Continuation continuation) { ... }
});

continuation.suspend();
...
}

Прослушиватели продолжения аналогичны Serynlet 3.0 AsyncListeners.

 

Шаблоны продолжения

Приостановить шаблон возобновления


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

  void doGet(HttpServletRequest request, HttpServletResponse response)
{
// if we need to get asynchronous results
Object results = request.getAttribute("results);
if (results==null)
{
final Continuation continuation = ContinuationSupport.getContinuation(request);

// if this is not a timeout
if (continuation.isExpired())
{
sendMyTimeoutResponse(response);
return;
}

// suspend the request
continuation.suspend(); // always suspend before registration

// register with async service. The code here will depend on the
// the service used (see Jetty HttpClient for example)
myAsyncHandler.register(new MyHandler()
{
public void onMyEvent(Object result)
{
continuation.setAttribute("results",results);
continuation.resume();
}
});
return; // or continuation.undispatch();
}

// Send the results
sendMyResultResponse(response,results);
}

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

Приостановить продолжение

The suspend/complete style is used when an asynchronous handler is used to generate the response:

  void doGet(HttpServletRequest request, HttpServletResponse response)
{
final Continuation continuation = ContinuationSupport.getContinuation(request);

// if this is not a timeout
if (continuation.isExpired())
{
sendMyTimeoutResponse(request,response);
return;
}

// suspend the request
continuation.suspend(response); // response may be wrapped.

// register with async service. The code here will depend on the
// the service used (see Jetty HttpClient for example)
myAsyncHandler.register(new MyHandler()
{
public void onMyEvent(Object result)
{
sendMyResultResponse(continuation.getServletResponse(),results);
continuation.complete();
}
});
}

Этот стиль очень хорош, когда ответ не нуждается в средствах контейнера сервлета (например, он не использует веб-среду) и если событие возобновит только одно продолжение. Если нужно отправить много ответов (например, в чат), то написание одного ответа может заблокировать и вызвать DOS для других ответов.

 

Примеры продолжения

Сервлет чата


В
ChatServlet пример показывает , как приостановить / возобновить стиль может быть использован для прямого кода в комнате чата. Те же принципы применяются к таким фреймворкам, как
cometd.org, которые предоставляют более богатую среду для таких приложений на основе Continuations.

Фильтр качества обслуживания

QoSFilter (
Javadoc ), использование приостановки / стиль резюме , чтобы ограничить количество запросов одновременно внутри фильтра. Это можно использовать для защиты пула соединений JDBC или другого ограниченного ресурса от слишком большого количества одновременных запросов.

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

Фильтр отказа в обслуживании

DosFilter (
Javadoc ) похож на QoSFilter, но защищает веб — приложения от атаки отказа в обслуживании (как можно лучше изнутри веб — приложения). Если из одного источника обнаружено слишком много запросов, эти запросы приостанавливаются и генерируется предупреждение. Это работает в предположении, что злоумышленник может быть написан в простом стиле блокировки, поэтому, если вы приостановите работу, вы надеетесь использовать их ресурсы. Истинная защита от DOS может быть достигнута только сетевыми устройствами (или евгеникой :).

Прокси Сервлет

ProxyServlet использует приостановить / полный стиль и причал асинхронного
клиент для реализации масштабируемого прокси — сервера (или прозрачного прокси).

Gzip фильтр

Причал GzipFilter представляет собой фильтр , который реализует динамическое сжатие путем обертывания объектов отклика. Этот фильтр был улучшен, чтобы понимать продолжения, поэтому, если запрос приостановлен в стиле приостановки / завершения, и упакованный ответ передается асинхронному обработчику, тогда ContinuationListener используется для завершения упакованного ответа. Это позволяет GzipFilter работать с асинхронным ProxyServlet и сжимать прокси-ответы.

 

Где ты это взял?

Вы можете прочитать об этом , или скачать его с Jetty или включить его в свой проект Maven, как этот pom.xml

С http://blogs.webtide.com