Practice of springboot websocket

Time:2021-3-4

What is websocket

Websocket is a protocol for full duplex communication over a single TCP connection. After the websocket connection is successful, the server and the client can communicate in two directions. In the scenario of message push, websocket can save server resources and bandwidth better than polling, and can communicate in real time.

Practice of springboot websocket

  • It has good compatibility with HTTP protocol. The default port is also 80 and 443, and the handshake phase uses HTTP protocol, so it is not easy to shield the handshake, and it can pass through various HTTP proxy servers.
  • TCP dependent
  • The data format is light, the performance overhead is small, and the communication is efficient.
  • You can send text or binary data.
  • There is no source restriction, the client can communicate with any server.
  • The protocol identifier is WS (or WSS if encrypted), and the server address is the URL.

Using websocket in springboot

After a brief understanding of websocket, let’s put it into practice. There are many ways to implement websocket server in springboot. Here I choose Tomcatjavax.websocket.serverThe API of,The demo address will be given at the end

  1. Introducing Maven dependency
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
  1. Create a bean to process the websocket request, and declare the websocket URL accepted by the current bean through the serverendpoint

Why @ controller is declared here will be explained later

import org.springframework.stereotype.Controller;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/message_websocket")
@Controller
public class MsgWebsocketController {

    @OnOpen
    public void onOpen(Session session) {
        //Authentication first. If authentication passes, websocketsession will be stored. Otherwise, the connection will be closed. The authentication code is omitted here 
        WebSocketSupport.storageSession(session);
        System.out.println("session open. ID:" + session.getId());
    }

    /**
     *Method called by connection close
     */
    @OnClose
    public void onClose(Session session) {
        System.out.println("session close. ID:" + session.getId());
    }

    /**
     * call method after receiving client message
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("get client msg. ID:" + session.getId() + ". msg:" + message);
    }

    /**
     *Called when an error occurs
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

}
  1. Declare serverendpoint exporter
@Configuration
public class WebsocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

So far, the websocket server has been built, and the client can communicate with the server

The server pushes messages to the client throughsession.getBasicRemote().sendText(message);that will do

Analysis of source code

Let’s take a look at how these short lines of code build websocket server for us

ServerEndpointExporter

Practice of springboot websocket

Focus on the content in the red box below

  1. ServerEndpointExporter implements SmartInitializingSingleton, which will be called after the bean instantiation is finished.afterSingletonsInstantiated
  2. Get all tags from the spring context@ServerEndpointThe name of the bean

In fact, the msgwebsocketcontroller we declare is not only marked with @ controller, but just to register it in the spring container to facilitate the registration of serverendpoint. The mark @ controller is more in line with the spring development specification

3 ~ 4. Mark all tags through servercontainer@ServerEndpointBean registration for

The default implementation class of servercontainer is wsservercontainer. It will map our serverendpoint, url = > the corresponding class, and then call the specified method for different events (for example, call the tag when establishing a connection)@OnopenThis is a little spring dispatcher servlet flavor, interested students can take a look at it

After learning what spring has done for us, let’s refine our demo

Set up a session manager

When we want to push messages to the client, first we need to find the connection between the client and the server, that is, websocketsession

Although websocketsession has been stored in wsservercontainer, there is no way to locate the specified session directly through the sessionid or our business ID, so we need to implement our own session manager

final ConcurrentHashMap<Object, Session> sessionPool = new ConcurrentHashMap<>();

Use concurrent HashMap to manage

Distributed push solution

Practice of springboot websocket

As shown in the figure, user 1 establishes websocket with server a, and user 2 establishes websocket with server B. how can user 1 push a message to user 2?

Websocketsession is actually a network connection. Unlike our traditional session, which can be serialized to redis, only each server can manage its own websocketsession. Therefore, server a notifies server B that you want to push a message to user 2.

A relatively simple and effective implementation method, using message queue, as shown in the following figure

Practice of springboot websocket

The advantage of this scheme is that it is easy to implement, but the disadvantage is that each server needs to judge whether there is a specified websocket session. If the scheme is detailed, it needs to maintain the relationship between the user session and each server, so that the message can be directly pushed to the specified server

Other issues

During the test, it is found that when the client is disconnected, the server can still call the session push method, and the server will always hold the invalid session

At present, the solution is to set the maximum idle time of websocketsession(session.setMaxIdleTimeout(milliseconds);)After this time, the server will close the session. The front end sends a heartbeat packet regularly to maintain the session. When the above situation occurs, the server will not hold the session all the time

Full demo address

For details of demo, please refer to readme in the project address

Github 👉 https://github.com/TavenYin/taven-springboot-learning/tree/master/sp-websocket

Gitee 👉 https://gitee.com/yintianwen7/taven-springboot-learning/tree/master/sp-websocket

reference resources

http://www.ruanyifeng.com/blog/2017/05/websocket.html

Part of the code reference a brother’s blog, but due to a little long time, can’t find, here to say sorry

If you feel that you have gained something, you can pay attention to my official account.