2014-10-29 1 views
3

Моя цель - поддерживать длительный опрос для нескольких вызывающих веб-сервисов и отслеживать, какие вызывающие абоненты в настоящее время «припаркованы» на длинном опрос (т. е. связанный). «Длительный опрос» означает, что вызывающий абонент вызывает веб-службу, и сервер (веб-служба) не возвращается сразу, но удерживает вызывающего абонента в течение некоторого заданного периода времени (час в моем приложении) или возвращается раньше если сервер имеет сообщение для отправки вызывающему абоненту (в этом случае сервер возвращает сообщение, вызывая asyncResponse.resume («MESSAGE»)).Джерси/JAX-RS 2 AsyncResponse - как отслеживать текущих абонентов с длинным опросом

Я сломаю это на два вопроса.

Первый вопрос: это разумный способ «припарковать» вызывающих абонентов, которые давно опросили?

@GET 
@Produces(MediaType.TEXT_PLAIN) 
@ManagedAsync 
@Path("/poll/{id}") 
public Response poller(@Suspended final AsyncResponse asyncResponse, @PathParam("id") String callerId) { 

    // add this asyncResponse to a HashMap that is persisted across web service calls by Jersey. 
    // other application components that may have a message to send to a caller will look up the 
    // caller by callerId in this HashMap and call resume() on its asyncResponse. 
    callerIdAsyncResponseHashMap.put(callerId, asyncResponse); 

    asyncResponse.setTimeout(3600, TimeUnit.SECONDS); 
    asyncResponse.setTimeoutHandler(new TimeoutHandler() { 
     @Override 
     public void handleTimeout(AsyncResponse asyncResponse) { 
      asyncResponse.resume(Response.ok("TIMEOUT").build()); 
     } 
    }); 
    return Response.ok("COMPLETE").build(); 
} 

Это работает нормально. Я просто не уверен, что это соответствует лучшим практикам. Кажется странным иметь строку «return Response ...» в конце метода. Эта строка выполняется, когда вызывающий абонент сначала подключается, но, как я понимаю, результат «COMPLETE» никогда не возвращается пользователю. Вызывающий абонент получает ответ «TIMEOUT» или какое-либо другое ответное сообщение, отправленное сервером через asyncResponse.resume(), когда серверу необходимо уведомить вызывающего абонента о событии.

Второй вопрос: мой текущий вызов состоит в том, чтобы точно отразить население в настоящее время опрошенных абонентов в HashMap. Когда вызывающий абонент прекращает опрос, мне нужно удалить его запись из HashMap. Вызывающий может уйти по трем причинам: 1) истекает 3600 секунд, и поэтому время истекает, 2) другой компонент приложения ищет вызывающего абонента в HashMap и вызывает asyncResponse.resume («MESSAGE»), и 3) HTTP-соединение по какой-то причине, например, кто-то выключил компьютер, на котором запущено клиентское приложение.

Итак, у JAX-RS есть два обратных вызова, которые я могу зарегистрировать для получения уведомлений о завершении соединений: CompletionCallback (для моих причин для подсчета голосов № 1 и № 2 выше) и ConnectionCallback (для моей причины 3-го опроса выше).

я могу добавить их в мой метод веб-службы, как это:

@GET 
@Produces(MediaType.TEXT_PLAIN) 
@ManagedAsync 
@Path("/poll/{id}") 
public Response poller(@Suspended final AsyncResponse asyncResponse, @PathParam("id") String callerId) { 

    asyncResponse.register(new CompletionCallback() { 
     @Override 
     public void onComplete(Throwable throwable) { 
      //? 
     } 
    }); 

    asyncResponse.register(new ConnectionCallback() { 
     @Override 
     public void onDisconnect(AsyncResponse disconnected) { 
      //? 
     } 
    }); 

    // add this asyncResponse to a HashMap that is persisted across web service calls by Jersey. 
    // other application components that may have a message to send to a caller will look up the 
    // caller by callerId in this HashMap and call resume() on its asyncResponse. 
    callerIdAsyncResponseHashMap.put(callerId, asyncResponse); 

    asyncResponse.setTimeout(3600, TimeUnit.SECONDS); 
    asyncResponse.setTimeoutHandler(new TimeoutHandler() { 
     @Override 
     public void handleTimeout(AsyncResponse asyncResponse) { 
      asyncResponse.resume(Response.ok("TIMEOUT").build()); 
     } 
    }); 
    return Response.ok("COMPLETE").build(); 
} 

Проблема, как я сказал, чтобы использовать эти две функции обратного вызова для удаления не-более избирательных звонящих из HashMap. ConnectionCallback на самом деле легче из двух. Так как он получает экземпляр asyncResponse в качестве параметра, я могу использовать, чтобы удалить соответствующую запись из HashMap, как это:

asyncResponse.register(new ConnectionCallback() { 
    @Override 
    public void onDisconnect(AsyncResponse disconnected) { 
     Iterator<Map.Entry<String, AsyncResponse>> iterator = callerIdAsyncResponseHashMap.entrySet().iterator(); 
     while (iterator.hasNext()) { 
      Map.Entry<String, AsyncResponse> entry = iterator.next(); 
      if (entry.getValue().equals(disconnected)) { 
       iterator.remove(); 
       break; 
      } 
     } 
    } 
}); 

Для CompletionCallback, хотя, так как asyncResponse уже сделано или отменены в то время обратный вызов запускается, в него не передается параметр asyncResponse. В результате, кажется, единственное решение состоит в том, чтобы запускать записи HashMap, проверяя их на выполненные/отмененные и удаляя их, как показано ниже. (Обратите внимание, что мне не нужно знать, покинул ли вызывающий абонент, потому что было вызвано resume(), или потому, что он был отключен, поэтому я не рассматриваю параметр «throwable»).

asyncResponse.register(new CompletionCallback() { 
    @Override 
    public void onComplete(Throwable throwable) { 
     Iterator<Map.Entry<String, AsyncResponse>> iterator = callerIdAsyncResponseHashMap.entrySet().iterator(); 
     while (iterator.hasNext()) { 
      Map.Entry<String, AsyncResponse> entry = iterator.next(); 
      if (entry.getValue().isDone() || entry.getValue().isCancelled()) { 
       iterator.remove(); 
      } 
     } 
    } 
}); 

Любая обратная связь будет оценена по достоинству. Является ли этот подход разумным? Есть ли лучший или более Джерси/JAX-RS способ сделать это?

ответ

0

Ваш метод poller() не должен возвращать ответ для участия в асинхронной обработке. Он может вернуть пустоту. Если вы делаете что-либо сложное в poller, однако вы должны рассмотреть возможность переноса всего метода в блок try/catch, который возобновляет ваш объект AsyncResponse, за исключением того, что любые RuntimeExceptions или другие непроверенные Throwables не теряются.Регистрация этих исключений в блоке catch здесь также кажется хорошей идеей.

В настоящее время я изучаю вопрос о том, как надежно улавливать асинхронный запрос, отмененный клиентом, и прочитал по одному вопросу, который предполагает, что механизм не работает на вопросника [1]. Я оставлю его другим, чтобы заполнить эту информацию на данный момент.

[1] AsyncResponse ConnectionCallback does not fire in Jersey

+0

Спасибо за ввод. Да, другой вопрос, который вы даете ссылку, тоже от меня. :) Кстати, я только что перешел от долгого опроса к серверным событиям (SSE), из-за проблемы с ConnectionCallback, не стреляющей. Похоже, нам все равно подходит для нас. Посмотрим. – ricb

Смежные вопросы