3 回答
TA贡献1877条经验 获得超6个赞
是的!我经历过这种情况,经过一些深度调试后,我得出结论,这只是 Heroku 路由器引擎中的“误报”。我的调试是这样的:
在客户端和 Heroku 上运行的 WebSocket 服务器之间创建一个 WebSocket 连接。
如果您使用 socket.io 或类似的库,它很可能会在将协议切换到 WebSocket 之前建立 HTTP 轮询连接。(您可以在 Heroku 路由器日志中看到这些 GET/POST 请求)。
一旦使用 WebSocket 协议建立连接,客户端将通过发送心跳保持连接(实际上服务器也可以这样做)。最初,您不会在 Heroku 路由器日志中看到此请求,因为它仍处于挂起状态(它处于打开状态并每隔几秒发送一次数据包,即心跳)。您可以使用 Chrome DevTools Network 选项卡来监控这一点。
心跳会阻止 Heroku 路由器终止请求,因为每次在客户端和服务器之间传输几个字节时,Heroku 路由器超时计时器(55 秒)都会重置。
但是,每当您的客户端关闭连接时(即:关闭浏览器选项卡、刷新页面、断开互联网连接等);Heroku 路由器检测到请求被终止(这是我基于调试的猜测)因为请求是“待处理”,它的反应好像它空闲了 X 毫秒,其中 X 是从收到请求开始的时间直到它关闭,你才会看到这样的日志:
service=79859ms status=101
结论:每当您的客户端应用程序终止 WebSocket 连接时,Heroku 路由器都会记录错误 15 行,该连接在 X 毫秒内运行良好。因此,在许多情况下,可能只是用户离开了您的应用。
我希望这对人们有所帮助,并且你们可以少担心一件事情去睡觉:)
TA贡献1155条经验 获得超0个赞
我通过每秒从服务器发送 ping 来解决这个问题,就像在 heroku nodejs 示例中一样:https : //github.com/heroku-examples/node-websockets/blob/master/server.js
TA贡献2080条经验 获得超4个赞
如果有人来这里从Java角度寻找解决方案:
@Component
public class SocketHandler extends TextWebSocketHandler {
public static Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String userName = (String) session.getAttributes().get("userName");
sessions.put(userName, session);
super.afterConnectionEstablished(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String userName = (String) session.getAttributes().get("userName");
sessions.remove(userName);
super.afterConnectionClosed(session, status);
}
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(), "/webSocket/AppName/*").addInterceptors(auctionInterceptor());;
}
@Bean
public HandshakeInterceptor auctionInterceptor() {
return new HandshakeInterceptor() {
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// Get the URI segment corresponding to the auction id during handshake
String path = request.getURI().getPath();
String userName = path.substring(path.lastIndexOf('/') + 1);
// This will be added to the websocket session
attributes.put("userName", userName);
return true;
}
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
// Nothing to do after handshake
}
};
}
}
以上是我在 Spring boot 中的 WebSocket 配置,因为我只想创建连接并将其保存在map用户名和会话详细信息中。
之后在 Springboot main 方法中每 30 秒添加一次 ping,如下所示:
public static void main(String[] args) {
SpringApplication.run(MayAppName.class, args);
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable r = () -> {
if(SocketHandler.sessions != null && !SocketHandler.sessions.isEmpty()) {
for (Map.Entry<String, WebSocketSession> stringWebSocketSessionEntry : SocketHandler.sessions.entrySet()) {
try {
// Dummy ping to keep connection alive.
stringWebSocketSessionEntry.getValue().sendMessage(new TextMessage("Dummy Ping"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
scheduler.scheduleAtFixedRate(r, 0, 30, TimeUnit.SECONDS);
}
这样做的作用是,它每 30 秒不断 ping 订阅者,以便默认55秒数不会影响 websocket 连接,如 Heroku 文档中所建议的那样。
Timeouts
The normal Heroku HTTP routing timeout rules apply to WebSocket connections. Either client or server can prevent the connection from idling by sending an occasional ping packet over the connection.
- 3 回答
- 0 关注
- 151 浏览
添加回答
举报