问题
I have a Go service serving as a web socket server on Heroku. The client pings the server every 20 seconds and it seems to keep the connection open. The problem is that when the socket connection is closed, Heroku router throws H15 error thinking that the request took too much time. For example, if web socket connection has been open for 300 seconds, Heroku log would show:
….H15…. dyno=web.1 connect=1ms service=300000ms status=503 bytes=147….
Anyone has experienced this?
回答1:
I solved the problem by sending pings from the server every second, as in the heroku nodejs example: https://github.com/heroku-examples/node-websockets/blob/master/server.js
回答2:
If anyone comes here looking for a solution from Java perspective:
@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
}
};
}
}
Above is my WebSocket configuration in Spring boot, as I only want the connection to be created and keep it in a map with userName and session details.
After that in Springboot main method just added a ping every 30 seconds like this :
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);
}
What this does is, it keeps pinging the subscriber every 30 seconds so that the default 55 seconds does not affect websocket connection, as suggested in Heroku docs.
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.
来源:https://stackoverflow.com/questions/32728030/heroku-h15-error-on-web-socket-close