websocket默认心跳等待300秒

本文详细介绍了WebSocket服务器的实现方式,包括连接建立、关闭、接收消息和错误处理的方法。阐述了如何使用Spring框架管理WebSocket连接,跟踪在线用户数量,并处理与业务服务的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@ServerEndpoint(value="/websocket/{param}",configurator = GetHttpSessionConfigurator.class)
@Component
public class WebSocketServer {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 连接建立成功调用的方法
     * 
     *  1000 连接正常关闭
        1001 端点离线,例如服务器down,或者浏览器已经离开此页面
        1002 端点因为协议错误而中断连接
        1003 端点因为受到不能接受的数据类型而中断连接
        1004 保留
        1005 保留, 用于提示应用未收到连接关闭的状态码
        1006 端点异常关闭
        1007 端点收到的数据帧类型不一致而导致连接关闭
        1008 数据违例而关闭连接
        1009 收到的消息数据太大而关闭连接
        1010 客户端因为服务器未协商扩展而关闭
        1011 服务器因为遭遇异常而关闭连接
        1015 TLS握手失败关闭连接
     * @param session
     *            可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     * @throws IOException 
     */
    @OnOpen
    public void onOpen(@PathParam(value = "param") String param, Session session) throws IOException {
        AccountSubService accountSubService=(AccountSubService)SpringContextUtil.getBean("accountSubServiceImpl");
        try {
            session.setMaxIdleTimeout(100000);//心跳等待时间
            logger.info("有新连接加入{},超时时间:{},当前时间:{}",param,session.getMaxIdleTimeout(),System.currentTimeMillis());
            addOnlineCount(); // 在线数加1
            
            WebSocketMap.getInstance().sessionMap.put(session.getId(), session);
            WebSocketMap.getInstance().sessionAccountMap.put(session.getId(), param);
            WebSocketMap.getInstance().accountSessionMap.put(param, session.getId());
            
            logger.info("有新连接加入{},当前在线人数为{},seeionid:{}",param,getOnlineCount(),session.getId());
            
            String[] params=param.split(Constants.SEPARATOR_CHAR_2);
            accountSubService.editOnlineStatus(BusinessConstants.ACCOUNT_SUB_ONLINE_STATUS_ONLINE, params[1]);//在线状态,子账户号
            
            ResponseModel responseModel=new ResponseModel();
            responseModel.setRequestId(session.getId());
            responseModel.setRetCode(RetCodeEnum.SUCCESS.getCode());
            responseModel.setRetMsg(RetCodeEnum.SUCCESS.getValue());
            responseModel.setSuccess(true);
            responseModel.setWsStatus(BusinessConstants.WEBSOCKET_STATUS_ONLIN);
            sendMessage(responseModel,session);
        } catch (IOException e) {
            logger.error("websocket-onOpen连接异常: 登录人{},Exception:{}",param,e.getMessage());
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        subOnlineCount(); // 在线数减1
        String param=WebSocketMap.getInstance().sessionAccountMap.get(session.getId());
        try {
            WebSocketMap.getInstance().sessionMap.remove(session.getId());
            WebSocketMap.getInstance().sessionAccountMap.remove(session.getId());
            WebSocketMap.getInstance().accountSessionMap.remove(param);
            logger.info("有一连接关闭!当前在线人数为{},seeionid:{},param:{},当前时间:{}",getOnlineCount(),session.getId(),param,System.currentTimeMillis());
            
            AccountSubService accountSubService=(AccountSubService)SpringContextUtil.getBean("accountSubServiceImpl");
            String[] params=param.split(Constants.SEPARATOR_CHAR_2);
            accountSubService.editOnlineStatus(BusinessConstants.ACCOUNT_SUB_ONLINE_STATUS_OFFLINE, params[1]);//在线状态,子账户号
            
            ResponseModel responseModel=new ResponseModel();
            responseModel.setRequestId(session.getId());
            responseModel.setRetCode(RetCodeEnum.FAIL.getCode());
            responseModel.setRetMsg(RetCodeEnum.FAIL.getValue());
            responseModel.setSuccess(false);
            responseModel.setWsStatus(BusinessConstants.WEBSOCKET_STATUS_CLOSE);
            sendMessage(responseModel,session);
        } catch (Exception e) {
             logger.error("websocket-onClose连接异常: 登录人{},Exception:{}",param,e.getMessage());
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * 
     * @param message
     *            客户端发送过来的消息
     * @param session
     *            可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        String param=WebSocketMap.getInstance().sessionAccountMap.get(session.getId());
        try {
            logger.info("来自客户端的消息!message:{},sessionId:{},param:{},当前时间:{}",message,session.getId(),param,System.currentTimeMillis());
            WsAsyncBussService wsAsyncBussService=(WsAsyncBussService)SpringContextUtil.getBean("wsAsyncBussService");
            ResponseModel responseModel=wsAsyncBussService.recivedMessage(message);
        } catch (Exception e) {
            logger.error("websocket-onMessage接收消息失败: 登录人{},Exception:{}",param,e.getMessage());
        }
    }

    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        subOnlineCount(); // 在线数减1
        String param=WebSocketMap.getInstance().sessionAccountMap.get(session.getId());
        try {
            WebSocketMap.getInstance().sessionMap.remove(session.getId());
            WebSocketMap.getInstance().sessionAccountMap.remove(session.getId());
            WebSocketMap.getInstance().accountSessionMap.remove(param);
            logger.info("有一连接发生错误!当前在线人数为{},seeionid:{},param:{},当前时间:{}",getOnlineCount(),session.getId(),param,System.currentTimeMillis());
            
            AccountSubService accountSubService=(AccountSubService)SpringContextUtil.getBean("accountSubServiceImpl");
            String[] params=param.split(Constants.SEPARATOR_CHAR_2);
            accountSubService.editOnlineStatus(BusinessConstants.ACCOUNT_SUB_ONLINE_STATUS_OFFLINE, params[1]);//在线状态,子账户号
            
            ResponseModel responseModel=new ResponseModel();
            responseModel.setRequestId(session.getId());
            responseModel.setRetCode(RetCodeEnum.FAIL.getCode());
            responseModel.setRetMsg(RetCodeEnum.FAIL.getValue());
            responseModel.setSuccess(false);
            responseModel.setWsStatus(BusinessConstants.WEBSOCKET_STATUS_ERROR);
            sendMessage(responseModel,session);
        } catch (Exception e) {
            logger.error("websocket-onError连接异常: 登录人{},Exception:{}",param,e.getMessage());
        }
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     */
    public void sendMessage(String message,Session session) throws IOException {
        logger.info("发送的消息:{}",message);
        session.getBasicRemote().sendText(message);
    }
    
    public void sendMessage(ResponseModel responseModel,Session session) throws IOException {
        logger.info("发送的消息:{}",JSON.toJSONString(responseModel));
        session.getBasicRemote().sendText(JSON.toJSONString(responseModel));
    }

    public static int getOnlineCount() {
        return WebSocketMap.onlineCount.get();
    }

    public static void addOnlineCount() {
        WebSocketMap.onlineCount.incrementAndGet();//在线数加1
    }

    public static void subOnlineCount() {
        WebSocketMap.onlineCount.decrementAndGet();//在线数减1
    }
}
 

<think>好的,我需要帮助用户解决在Nginx中配置WebSocket心跳机制的问题。首先,我得回顾一下用户提供的参考资料和之前的问题。用户提到他们遇到WebSocket连接大约一分钟后中断,这通常与Nginx的超时设置有关。他们提供了三个引用,其中引用2和3提到了proxy_http_version、Upgrade和Connection头,以及proxy_read_timeout的设置。 首先,WebSocket需要保持长连接,而Nginx默认的代理超时时间可能太短,导致连接被关闭。心跳机制通常用于保持连接活跃,防止被中间设备(如Nginx或防火墙)断开。在Nginx中,虽然不能直接配置心跳,但可以通过调整超时参数来模拟心跳的效果。 接下来,我需要确定哪些Nginx配置参数是关键。根据引用2和3,proxy_read_timeout是控制连接空闲时间的,默认可能是60,导致一分钟后断开。将其设置为更大的值,比如36000(10小时),可以避免频繁超时。同时,确保正确设置Upgrade和Connection头,以便Nginx正确处理WebSocket的升级请求。 然后,用户可能需要一个配置示例。结合引用中的例子,我应该提供一个包含proxy_pass、proxy_http_version 1.1、Upgrade和Connection头,以及调整proxy_read_timeout、proxy_connect_timeout和proxy_send_timeout的配置片段。此外,建议在应用层实现心跳机制,比如定期发送ping/pong帧,这样即使Nginx的超时设置较长,也能确保连接活跃。 还需要注意,不同的Nginx版本可能有不同的行为,确保版本较新以避免已知的问题。最后,测试配置是否有效,使用工具检查连接是否持久,并监控Nginx的日志以确认没有错误。 总结步骤:调整超时参数,正确设置HTTP升级头,提供配置示例,建议应用层心跳,版本检查,测试验证。这样用户应该能解决WebSocket连接中断的问题了。</think>在Nginx中配置WebSocket心跳机制的核心在于调整超时参数和保持连接活跃性。以下是具体配置方法及示例: ### 一、Nginx WebSocket代理基础配置 1. **HTTP协议升级** 需明确设置协议升级头和HTTP版本: ```nginx proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; ``` 2. **超时参数调优** - `proxy_read_timeout`:控制等待后端响应的空闲时间,建议设为`3600s`(1小时)或更高[^2][^3] - `proxy_send_timeout`:定义向后端发送数据的超时时间,建议`60s` - `proxy_connect_timeout`:后端连接超时,建议`5s` ### 二、完整配置示例 ```nginx server { listen 80; server_name ws.example.com; location /websocket/ { proxy_pass http://backend_server; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; # 超时设置(单位:) proxy_connect_timeout 5; proxy_read_timeout 3600; # 关键参数,防止Nginx主动断开 proxy_send_timeout 60; # 保持连接活跃(可选) proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` ### 三、心跳机制实现方案 1. **Nginx层保活** 通过调整`proxy_read_timeout`到较大值(如`36000s`)模拟心跳效果 2. **应用层心跳** 推荐在WebSocket服务端和客户端实现: ```javascript // 客户端示例(JavaScript) const ws = new WebSocket('wss://ws.example.com'); const heartbeatInterval = 55000; // 55(小于Nginx超时时间) setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'heartbeat' })); } }, heartbeatInterval); ``` ### 四、验证与调试 1. 使用`curl`测试连接: ```bash curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" http://ws.example.com ``` 2. 查看Nginx日志: ```nginx access_log /var/log/nginx/websocket.log; error_log /var/log/nginx/websocket_error.log debug; ``` ### 五、注意事项 1. 防火墙设置需放行WebSocket端口(通常80/443) 2. 若使用SSL,需配置`wss://`协议: ```nginx listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/privkey.pem; ``` 3. 建议Nginx版本不低于1.14(支持WebSocket代理增强特性)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qujianjiutian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值