文章目录
- 原理
- 配置
- 附件
- Java_websocket空闲检测原理
Spring Websocket 是基于 WebSocket 协议的实现,它提供了一种在客户端和服务器之间实时双向通信的方式。其中,idle check(空闲检查)是一种机制,用于检测 WebSocket 连接的空闲状态。
Spring 框架本身并没有提供内置的主动发送 Ping 消息的机制。WebSocket 协议本身定义了 Ping 和 Pong 消息用于心跳检测,但在 Spring 中,默认情况下并不会主动发送 Ping 消息。
其仅仅提供了定时的读写check,例如120秒内是否有读或者写。
Java_websocket提供了主动发送Ping消息的机制,原理在下面有。
原理
1.在第一次websocket 连接时,启动一个后台定时任务
org.apache.tomcat.websocket.BackgroundProcessManager.register
if (processes.size() == 0){wsBackgroundThread = new WsBackgroundThread();wsBackgroundThread.start();
}
2.其每秒执行一次,尝试去处理
org.apache.tomcat.websocket.BackgroundProcessManager
private static class WsBackgroundThread extends Thread {@Overridepublic void run() {while (running) {try {Thread.sleep(1000);} catch (InterruptedException e) {// Ignore}manager.process();}}
3.每秒调用一次backgroundProcess,当backgroundProcessCount>=10时继续处理
即等于每10秒check一次
org.apache.tomcat.websocket.WsWebSocketContainer.backgroundProcess
public void backgroundProcess() {backgroundProcessCount++;if (backgroundProcessCount >= processPeriod) {backgroundProcessCount = 0;for (WsSession wsSession : sessions.keySet()) {wsSession.checkExpiration();}}
4.检查是否过期
org.apache.tomcat.websocket.WsSession.checkExpiration
protected void checkExpiration() {// Local copies to ensure consistent behaviour during method executionlong timeout = maxIdleTimeout;long timeoutRead = getMaxIdleTimeoutRead();long timeoutWrite = getMaxIdleTimeoutWrite();long currentTime = System.currentTimeMillis();String key = null;if (timeoutRead > 0 && (currentTime - lastActiveRead) > timeoutRead) {key = "wsSession.timeoutRead";} else if (timeoutWrite > 0 && (currentTime - lastActiveWrite) > timeoutWrite) {key = "wsSession.timeoutWrite";} else if (timeout > 0 && (currentTime - lastActiveRead) > timeout &&(currentTime - lastActiveWrite) > timeout) {key = "wsSession.timeout";}if (key != null) {String msg = sm.getString(key, getId());if (log.isDebugEnabled()) {log.debug(msg);}doClose(new CloseReason(CloseCodes.GOING_AWAY, msg), new CloseReason(CloseCodes.CLOSED_ABNORMALLY, msg));}}
配置
配置代码为:container.setMaxSessionIdleTimeout(10 * 1000L);
整体示例如下:
@Configuration
@EnableWebSocket // 启动Websocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(myHandler(), "/websocket/**")// 添加拦截器,可以获取连接的param和 header 用作认证鉴权.addInterceptors(new LakerSessionHandshakeInterceptor())// 设置运行跨域.setAllowedOrigins("*");}@Beanpublic ServletServerContainerFactoryBean createWebSocketContainer() {ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();// 设置默认会话空闲超时 以毫秒为单位 非正值意味着无限超时,默认值 0 ,默认没10s检查一次空闲就关闭container.setMaxSessionIdleTimeout(10 * 1000L);// 设置异步发送消息的默认超时时间 以毫秒为单位 非正值意味着无限超时 ,默认值-1,还没看到作用
// container.setAsyncSendTimeout(10 * 1000L);// 设置文本消息的默认最大缓冲区大小 以字符为单位,默认值 8 * 1024container.setMaxTextMessageBufferSize(8 * 1024);// 设置二进制消息的默认最大缓冲区大小 以字节为单位,默认值 8 * 1024container.setMaxBinaryMessageBufferSize(8 * 1024);return container;}
附件
Java_websocket空闲检测原理
连接丢失检查是一种检测与另一个端点的连接是否丢失的功能,例如由于wifi或移动数据信号丢失。
为了检测丢失的连接,我们使用心跳实现。
检测以指定的时间间隔(例如:60 秒)运行,并对所有连接的端点执行以下操作:
- 如果端点最近没有发送 pong,则断开端点。端点被给予 1.5 倍的时间间隔来回复PONG。因此,如果间隔为 60 秒,则端点有 90 秒的响应时间。
- 向端点发送 ping。
检测是双向的,因此服务器可以检测到丢失的客户端,而客户端可以检测到与服务器的连接丢失。
端点应该在可行的情况下尽快用 Pong 帧响应 Ping 帧。
代码
// 设置间隔为120秒
server.setConnectionLostTimeout(120);
// 间隔小于或等于 0 的值会导致检查被停用。
server.setConnectionLostTimeout( 0 );
启动定时任务
if (this.connectionLostTimeout <= 0) {cancelConnectionLostTimer();return;
}
connectionLostCheckerFuture = connectionLostCheckerService.scheduleAtFixedRate(connectionLostChecker, connectionLostTimeout, connectionLostTimeout,TimeUnit.NANOSECONDS);
定时内容,check上传pong时间及发送Ping消息
Runnable connectionLostChecker = new Runnable() {@Overridepublic void run() {// 实现原理 核心代码如下long minimumPongTime;synchronized (syncConnectionLost) {minimumPongTime = (long) (System.nanoTime() - (connectionLostTimeout * 1.5));}for (WebSocket webSocket : connections) {WebSocketImpl webSocketImpl = (WebSocketImpl) webSocket;if (webSocketImpl.getLastPong() < minimumPongTime) {// 如果最后一次收到PONG的时间差值 小于了 1.5倍的设置值// 则关闭连接log.trace("Closing connection due to no pong received: {}", webSocketImpl);webSocketImpl.closeConnection(CloseFrame.ABNORMAL_CLOSE)} else {// 为客户端发送Pingif (webSocketImpl.isOpen()) {webSocketImpl.sendPing();} else {log.trace("Trying to ping a non open connection: {}", webSocketImpl);}}}