У меня есть стандартная конечная точка веб-сокета, основанная на реализации Tyrus, которая время от времени запускает java.lang.IllegalStateException: Cannot set WriteListener for non-async or non-upgrade request
. Мы работаем на Payara 4.1.
Моя стандартная реализация
@ServerEndpoint(value = "...", decoders=MessageDecoder.class, encoders=MessageEncoder.class)
public class EndpointImpl extends AbstractEndpoint{
// onOpen, onClose, onMessage, onError methods
}
Где находится абстрактный класс
public abstract class AbstractEndpoint{
// irrelevant onOpen, onOpen handling method
117 protected void sendMessage(Session session, Message message){
118 if(message == null){
119 LOGGER.error("null message");
120 } else if(!session.isOpen()){
121 LOGGER.error("session is not opened");
122 } else{
>>>123 session.getAsyncRemote().sendObject(message, (result) -> {
124 if (result.isOK()) {
125 LOGGER.info("success! yeah!");
126 } else {
127 LOGGER.error("error when sending message", result.getException());
128 }
129 });
130 }
}
}
Илегалстатеексцептион
Пока ничего особенного. Я могу отлично общаться и отвечать на полученный запрос, а с помощью веб-сокета FTW я могу передавать информацию и получать обратную связь. Однако я время от времени получаю исключение:
java.lang.IllegalStateException: Cannot set WriteListener for non-async or non-upgrade request
at org.apache.catalina.connector.OutputBuffer.setWriteListener(OutputBuffer.java:536)
at org.apache.catalina.connector.CoyoteOutputStream.setWriteListener(CoyoteOutputStream.java:223)
at org.glassfish.tyrus.servlet.TyrusServletWriter.write(TyrusServletWriter.java:140)
at org.glassfish.tyrus.core.ProtocolHandler.write(ProtocolHandler.java:486)
at org.glassfish.tyrus.core.ProtocolHandler.send(ProtocolHandler.java:274)
at org.glassfish.tyrus.core.ProtocolHandler.send(ProtocolHandler.java:332)
at org.glassfish.tyrus.core.TyrusWebSocket.sendText(TyrusWebSocket.java:317)
at org.glassfish.tyrus.core.TyrusRemoteEndpoint.sendSyncObject(TyrusRemoteEndpoint.java:429)
at org.glassfish.tyrus.core.TyrusRemoteEndpoint$Async.sendAsync(TyrusRemoteEndpoint.java:352)
at org.glassfish.tyrus.core.TyrusRemoteEndpoint$Async.sendObject(TyrusRemoteEndpoint.java:249)
at com.mycompany.websocket.AbstEndpoint.sendMessage(AbstEndpoint.java:123)
Вторая попытка метода sendMessage
Сначала я подумал, что моя асинхронная конечная точка была неправильно настроена, поэтому я попробовал способ Future‹> вместо обратного вызова:
RemoteEndpoint.Async async = session.getAsyncRemote();
async.setSendTimeout(5000); // 5 seconds
Future<Void> future = async.sendObject(message);
try{
future.get();
}
catch(InterruptedException | ExecutionException ex){
LOGGER.error("error when sending message", ex);
}
Я также получил исключение.
Пока и симптомы
Удивительно, но я нашел только одну ссылку, посвященную этой проблеме.
- Ссылка на github подчеркивает проблему с размером буфера. Я не использую частичные сообщения, только целые сообщения. Более того, независимо от того, использую ли я размер буфера по умолчанию или устанавливаю новый, возникает исключение
- Я не смог найти глобальное правило о том, как воспроизвести ошибку
- После возникновения исключения клиент мог продолжать отправлять сообщения, а сервер обрабатывал их, но сервер никогда не отвечал клиенту. Похоже, исходящий канал связи заблокирован
- Поскольку сервер продолжает обрабатывать входящие сообщения, канал веб-сокета не закрывается после исключения
Копаем в реализации Tyrus
Я просмотрел реализацию tyrus-core и обнаружил, что метод отправки зависит от какого-то компонента Grizzly. Я ничего не знаю о Grizzly, но похоже, что отправка должна быть синхронной в любом случае из-за некоторых ограничений Grizzly.
Вопросы
- Кто-то уже встречал такую ситуацию? Если да, действительно ли исключение означает, что где-то есть узкое место, или оно означает что-то еще?
- Является ли асинхронная конечная точка Tyrus действительно асинхронной, то есть как «обработать и забыть»?
- Я не нашел способа поставить в очередь исходящие сообщения: если сообщение A длинное, дождитесь завершения отправки сообщения A перед отправкой сообщения B. Есть ли способ обрабатывать большие сообщения в веб-сокете или асинхронная конечная точка является единственной способ?
- Я хочу убедиться, что при отправке не возникло никаких проблем, поэтому я выбрал асинхронное решение. Должен ли я вернуться к синхронному способу?
Я не стал подробно рассказывать о своем расследовании Тайруса. Если вы считаете это актуальным, не стесняйтесь спрашивать, и я с удовольствием разработаю.
IllegalStateException
. Так что это должно быть решение! Поскольку я не уверен во всех тонкостях, я подожду месяц, а затем опубликую ответ на свой вопрос, если он может помочь другим. В любом случае, большое спасибо @BalusC! - person Al-un   schedule 11.08.2017