Статьи

Обработка тайм-аутов в асинхронных запросах в JAX-RS

JAX-RS 2.0 обеспечивает поддержку парадигмы асинхронного программирования, как на клиенте, так и на стороне сервера. Этот пост, в котором описывается функция времени ожидания при выполнении асинхронных запросов REST на стороне сервера с использованием API JAX-RS (2.0)

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

  • необходимо указать экземпляр интерфейса AsyncResponse в качестве одного из параметров метода
  • аннотируйте его, используя аннотацию @Suspended (JAX-RS будет внедрять экземпляр AsyncResponse для вас всякий раз, когда обнаружит эту аннотацию)
  • необходимо вызвать запрос в другом потоке — рекомендуемый способ сделать это в Java EE 7 — использовать Managed Service Executor
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
@GET
@Produces("text/plain")
public void execute(@Suspended AsyncResponse response){
    System.out.println("Initially invoked on thread - "+ Thread.currentThread.getName() + ". This will free up soon !");
    new Thread(){
        @Override
        public void run(){
            response.resume("executed asynchronously on thread - "+ Thread.currentThread.getName());
        }
    }.start();
}
 
//JDK 8 version - passing a Runnable (in form of a Lambda Expression) to a thread
 
@GET
@Produces("text/plain")
public void execute(@Suspended AsyncResponse response){
    System.out.println("Initially invoked on thread - "+ Thread.currentThread.getName() + ". This will free up soon !");
    new Thread(() -> response.resume("executed asynchronously on thread - "+ Thread.currentThread().getName())).start();
}

За кулисами ??

Базовое соединение ввода / вывода между сервером и клиентом остается открытым. Но есть сценарии, в которых вы бы хотели, чтобы клиент не ждал ответа вечно. В таком случае вы можете выделить время ожидания (порог)

Поведение по умолчанию в случае тайм-аута — ответ HTTP 503 . Если вы хотите переопределить это поведение, вы можете реализовать TimeoutHandler и зарегистрировать его в AsyncResponse. В случае, если вы используете Java 8, вам не нужно беспокоиться о отдельном классе реализации или даже анонимном внутреннем классе — вы можете просто предоставить лямбда-выражение, поскольку TimeoutHandler — это функциональный интерфейс с единственным абстрактным методом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@GET
@Produces("text/plain")
public void execute(@Suspended AsyncResponse response){
    System.out.println("Initially invoked on thread - "+ Thread.currentThread.getName() + ". This will free up soon !");
    //just having this would result in HTTP 503 after 10 seconds
    response.setTimeout(10, TimeUnit.SECONDS);
    //client will recieve a HTTP 408 (timeout error) after 10 seconds
    response.setTimeoutHandler((asyncResp) -> asyncResp.resume(Response.status(Response.Status.REQUEST_TIMEOUT)).build());
    new Thread(() -> {
                try {
                    Thread.sleep(11000);
                } catch (InterruptedException ex) {
                   //ignoring
                }
            }).start();
}

Ура!