GAE/Java LocalChannelFailureException на сервере разработки
Я использую Channel API (Java) с Google App Engine для своего веб-приложения. Я реализовал механизм повторного использования токенов для быстрого превышения квот API канала. Это означает, что моя реализация повторно использует существующий канал для пользователя, который обновляет страницу, пока истекает срок действия токена, полученного ChannelService.createChannel()
звонок, еще не окончен
При обновлении моей страницы я получаю следующее исключение (с x
начиная с 0 и увеличивается для каждого обновления). Тем не менее, моя страница продолжает работать как задумано. Есть ли способ избежать исключения? Или я могу просто игнорировать исключение?
com.google.appengine.api.channel.dev.LocalChannelFailureException: Client connection with ID connection-x not found.
at com.google.appengine.api.channel.dev.Channel.getClientMessageQueue(Channel.java:79)
at com.google.appengine.api.channel.dev.ChannelManager.getNextClientMessage(ChannelManager.java:300)
at com.google.appengine.api.channel.dev.LocalChannelServlet.doGet(LocalChannelServlet.java:120)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
...
Я повторно использую токены со следующими классами:
При звонке ChannelService.createChannel()
Я сохраняю дату истечения срока действия и сгенерированный токен в сущности под названием "Канал"
public class Channel {
private String id;
private String token;
private Date expiration;
}
Затем у меня есть класс ChannelService, который возвращает действительный канал с его get()
метод. ChannelDAO - это класс, который просто использует Map для хранения каналов. Таким образом, отсутствует постоянство базы данных, что могло бы поддерживать токен при перезапуске сервера.
public Channel get(String clientId) {
Calendar calendar = Calendar.getInstance();
Channel channel = channelDAO.get(clientId);
if (channel == null || calendar.getTime().after(channel.getExpiration())) {
com.google.appengine.api.channel.ChannelService channelService = ChannelServiceFactory
.getChannelService();
calendar.add(Calendar.MINUTE, CHANNEL_UPTIME);
String token = channelService.createChannel(player.toString(), CHANNEL_UPTIME);
channel = new Channel(clientId, token, calendar.getTime());
channelDAO.persist(channel);
}
return channel;
}
3 ответа
Я исправил проблему путем дальнейших исследований источника исключения. Channel API работает с запросами на опрос, которые выполняются каждые 500 мс. Я использовал консоль Firefox для отслеживания этого. Вот пример опроса:
[20:40:15.978] GET http://localhost:8080/_ah/channel/dev?command=poll&channel=920a60f9b27ece1a1ba43d251fdacf2e-channel-eqt3xi-1385927324758-{clientId}&client=connection-2 [HTTP/1.1 200 OK 0ms]
В своем вопросе я заявил, что исключение возникает при перезагрузке страницы, поэтому проблема заключалась в следующем: когда страница перезагружается, что-то (я не знаю, что именно, но я предполагаю, что это как-то связано с закрытием сокетов и вновь открывается при обновлении страницы), что приводит к тому, что клиент (последний параметр запроса GET) перестает быть доступным. Тем не менее, доступен новый клиент: клиент "connection-{i+1}". Поэтому, когда вы входите на страницу изначально, клиент имеет "соединение-0". После обновления страницы это "соединение-1". Но поскольку старая страница использовала отложенное выполнение для опроса, на сервер отправляется ложный запрос (все еще connection-0), который в результате выдает исключение.
Я исправил проблему, вручную отменив отложенное выполнение, когда покинул страницу с помощью jQuery.
var channel = new goog.appengine.Channel('${channel.token}');
var socket = channel.open(handler);
$(window).on('beforeunload', function() {
clearTimeout(socket.pollingTimer_);
});
Ваша схема повторного использования токена должна быть тщательно проверена на наличие ошибок, поскольку это исключение не должно происходить при каждой перезагрузке страницы.
Существует известная проблема после перезапуска локального сервера, но, как указано, это должно быть только в случае перезапуска сервера разработки.
У меня была такая же проблема с использованием GWT и gwt-gae-channel. Решение будет что-то вроде:
Socket socket = channel.open(new SocketListener() {...});
Window.addWindowClosingHandler(new ClosingHandler() {
@Override
public void onWindowClosing(ClosingEvent event) {
socket.close();
}
});