Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper way to use Spring WebSocketConnectionManager when sessions closes

I use Spring's WebSocketConnectionManager to work with WebSockets. Sometimes the connection closes and I have to reconnect. But I didn't find any proper solution to do that. Can I use WebSocketConnectionManager for restore (reconnect to) session? In the sources I see, that connection establishes by the following code:

@Override
protected void openConnection() {
    if (logger.isInfoEnabled()) {
        logger.info("Connecting to WebSocket at " + getUri());
    }

    ListenableFuture<WebSocketSession> future =
            this.client.doHandshake(this.webSocketHandler, this.headers, getUri());

    future.addCallback(new ListenableFutureCallback<WebSocketSession>() {
        @Override
        public void onSuccess(@Nullable WebSocketSession result) {
            webSocketSession = result;
            logger.info("Successfully connected");
        }
        @Override
        public void onFailure(Throwable ex) {
            logger.error("Failed to connect", ex);
        }
    });
}

And I'm using this Manager when my spring boot application start by:

WebSocketConnectionManager connectionManager = new WebSocketConnectionManager(
    new StandardWebSocketClient(), webSocketHandler, "wss://localhost:8080/ws/");

connectionManager.start();

But what I have to do when connection closes? I tried to copy a piece of code from openConnection(), and use it for update the session directly in webSocketHandler, and it works. But it looks like dirty workaround.

Does anybody know how to do it correctly? Thank you.

like image 917
Arkady Avatar asked Aug 16 '18 09:08

Arkady


2 Answers

In one of my projects, I came around similar situation. So, I resorted on using start() and stop() method with access to WebSocketManager as a bean in a connection manager class. I'd created a sample project for websocketserver and websocketclient sometime back. Just added reconnect feature to it.

Also, I was not reconnecting on every connection close. Only on close status 1006(Abnormal Closure), 1011(Internal Error) and 1012(Service Restart).

The project can be directly downloaded and run.

like image 92
deep Avatar answered Nov 16 '22 17:11

deep


I don't think you need to copy the openConnection function directly in webSocketHandler. You should be able to re-use the openConnection function and capture the afterConnectionClose event from inside of WebSocketConnectionManager.

I think you are on the right track. Unfortunately, I don't think this is a dirty workaround due to the fact that nothing exists inside of WebSocketConnectionManager to handle WebSocket "on close" events.

In the docs for WebSocketConnectionManager I see this excerpt:

A WebSocket connection manager that is given a URI, a WebSocketClient, and a WebSocketHandler, connects to a WebSocket server through ConnectionManagerSupport.start() and ConnectionManagerSupport.stop() methods. If ConnectionManagerSupport.setAutoStartup(boolean) is set to true this will be done automatically when the Spring ApplicationContext is refreshed.

The equivalent of JavaScript's WebSocket onClose for Spring's WebSocketConnectionManager seems to be afterConnectionClosed which you can find in the docs for WebSocketHandler. Indeed, the docs on afterConnectionClosed say:

afterConnectionClosed - Invoked after the WebSocket connection has been closed by either side, or after a transport error has occurred. Although the session may technically still be open, depending on the underlying implementation, sending messages at this point is discouraged and most likely will not succeed.

Therefore, I think your Spring code needs to include something like the pseudo-code below:

    afterConnectionClosed(WebSocketSession result, CloseStatus closeStatus) {
        // Spring code here to delay retry for re-connecting to WebSocket. 
        openConnection()
    };

The JavaScript equivalent would be:

     ws.onclose = function(){
        // Try to reconnect in 5 seconds
        setTimeout(function(){start(websocketServerLocation)}, 5000);
    };

Hopefully someone with Spring experience can come along and edit this post and point out where I am correct and where I am mistaken. I don't use Spring but I have some WebSocket experience. In JavaScript I use reconnecting-websocket so my gut tells me you'll essentially need to implement this functionality in Spring if it does not already exist. Or as this answer suggests for JavaScript, you need to capture the onClose event from the WebSocket and reconnect.

Interestingly there are some methods named onClose in the Spring repo so perhaps this question needs revisiting.

like image 41
Scott Skiles Avatar answered Nov 16 '22 18:11

Scott Skiles