网上看到了一些关于这个错误的产生场景
我这里产生错误的场景是不同的,记录一下
背景
提供websocket服务的公司,其内部的系统已经与websocket server建立了长连接,图中的nginx是一个出口网关,其运维要求是出口网关与上游服务是不能建立长连接的。
而我们作为外部公司,希望使用其长连接服务,显然长连接是建立不起来了
方案
我们使用java开发websocket client,由于其不支持长连接,我们就采用了轮询的方式,每隔30s通过http upgrade来拉取一次数据
使用了@ClientEndpoint注解
伪代码
// 提供启动的时候 与nginx建立长连接
// 在onMessage方法中 每次发送http请求 获取的结果落库
这样做,在于nginx的长连接中不断发送http请求,是OK的
问题
问题是:在某些情况下会报错
[ ERROR] [2020-07-06 01:45:34] [WebSocketClient-AsyncIO-1] org.apache.tomcat.websocket.pojo.PojoEndpointBase [175] - No error handling configured for [xxx] and the following error occurred
java.io.IOException: java.util.concurrent.ExecutionException: java.io.IOException: 断开的管道
之后30秒报错
[ ERROR] [2020-07-04 15:41:42] [WebSocketClient-AsyncIO-1] org.apache.tomcat.websocket.pojo.PojoEndpointBase [175] - No error handling configured for [xxx] and the following error occurred
java.lang.IllegalStateException: The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method
通过这个错误,可能得出两个结论
1. 当websocket server与client之间的socket断开(broken pipe)之后,在长连接上根据session继续发送http请求会报错
java.lang.IllegalStateException: The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method
2. No error handling configured for 的原因是:没有配置@onError注解来处理
解决方案
是不是可以在onError这里重新建立一个新的socket连接呢?经过测试,这种方式是不行的,因为这样会导致前一个连接没有关闭,后面又建立了一个连接,导致多个端口对同一个remote endpoint建立连接
代码参考:
@OnError
public void onError(Session session, Throwable t) {
logger.error(CommonUtil.exceptionToString(t));
/**
* 解决websocket服务器断开socket 导致@ClientEndPoint生成的WebScoket-AsyncIO线程退出的问题
* 这里再次建立socket连接
*/
WebSocketContainer container;
try {
container = ContainerProvider.getWebSocketContainer();
URI r = URI.create(Const.SMK_WEBSOCKET_ADDRESS_PREFIX + Const.SMK_CENTER_MODULE);
session = container.connectToServer(this.getClass(), r);
} catch (DeploymentException | IOException ex) {
logger.error(CommonUtil.exceptionToString(ex));
}
}
应该在onClose方法中建立一个新的socket连接
@OnClose
public void onClose(Session session, CloseReason reason) {
logger.warn("closing session => {} - {}", session, reason);
/**
* 解决websocket服务器断开socket 导致@ClientEndPoint生成的WebScoket-AsyncIO线程退出的问题
* 这里再次建立socket连接
*/
try {
new Thread(new SmkCenterRunnable(), "smk-center-java-websocket-client").start();
} catch (Exception e) {
logger.error(CommonUtil.exceptionToString(e));
}
}
经过测试,大部分场景下其会重新建立其websocket连接。但是也存在失败的情况,后续可以加入retry策略,采用一个定时器,每隔1分钟,2分钟,4分钟...以及重试次数来尝试重新建立连接