Моя цель - поддерживать длительный опрос для нескольких вызывающих веб-сервисов и отслеживать, какие вызывающие абоненты в настоящее время «припаркованы» на длинном опрос (т. е. связанный). «Длительный опрос» означает, что вызывающий абонент вызывает веб-службу, и сервер (веб-служба) не возвращается сразу, но удерживает вызывающего абонента в течение некоторого заданного периода времени (час в моем приложении) или возвращается раньше если сервер имеет сообщение для отправки вызывающему абоненту (в этом случае сервер возвращает сообщение, вызывая 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 способ сделать это?
Спасибо за ввод. Да, другой вопрос, который вы даете ссылку, тоже от меня. :) Кстати, я только что перешел от долгого опроса к серверным событиям (SSE), из-за проблемы с ConnectionCallback, не стреляющей. Похоже, нам все равно подходит для нас. Посмотрим. – ricb