[Esquema de implementación] springboot usa websocket para comunicarse con el cliente en tiempo real (con sala de chat de varias personas + detección de latidos)

prefacio

En un proyecto anterior, existía la necesidad de realizar una comunicación en tiempo real, por lo que inicialmente entré en contacto con el protocolo websocket e hice una implementación comercial simple. Como comprensión preliminar, dé a cada uno una referencia.

1. ¿Por qué usar websocket?

En otras palabras, ¿qué problema resuelve websocket? La respuesta es que se resuelven dos problemas principales:

  • Solo los clientes pueden enviar solicitudes.
  • Envío frecuente de mensajes durante un período de tiempo

Suponiendo que es necesario diseñar un módulo de notificación de un sistema de alerta temprana en tiempo real, ¿cómo debemos, como ingenieros, diseñar la función de notificación? Si solo tenemos el protocolo http ahora, entonces solo podemos dejar que el cliente sondee el servidor continuamente.Cuanto menor sea el intervalo de sondeo, más cercano al efecto en tiempo real. Sin embargo, el sondeo es ineficiente y desperdicia recursos. Para tal escenario, surgió websocket.

2. conector web

El protocolo WebSocket nació en 2008 y se convirtió en un estándar internacional en 2011. Todos los navegadores ya lo soportan.
Su característica más importante es que el servidor puede enviar información de forma activa al cliente, y el cliente también puede enviar información de forma activa al servidor. Es un diálogo real de dos vías y pertenece a un tipo de tecnología de envío de servidor.
inserte la descripción de la imagen aquí
Otras características incluyen:
(1) Basado en el protocolo TCP, la implementación del lado del servidor es relativamente fácil y es un protocolo de transmisión confiable.
(2) Tiene buena compatibilidad con el protocolo HTTP. Los puertos predeterminados también son 80 y 443, y la fase de negociación utiliza el protocolo HTTP, por lo que no es fácil protegerse durante la negociación y puede pasar a través de varios servidores proxy HTTP.
(3) El formato de datos es relativamente ligero, la sobrecarga de rendimiento es pequeña y la comunicación es eficiente.
(4) Se pueden enviar texto o datos binarios.
(5) No hay restricción del mismo origen y el cliente puede comunicarse con cualquier servidor.
(6) El identificador de protocolo es ws (o wss si está encriptado) y la URL del servidor es la URL.

El contenido anterior está tomado de http://www.ruanyifeng.com/blog/2017/05/websocket.html

3. Implementación de websocket en springboot

Cargue directamente el código, omitiendo el paquete y la importación, y realice una demostración con múltiples salas de chat.

Entre ellos, es necesario comprender bien el procesamiento simultáneo del método onOpen y la estructura de datos de webSocketSet.

/**
 * websocket,实时信息回传。
 *
 * @author amber
 * @date 2020-08-05
 */

@Component
@ServerEndpoint("/websocket/{uniCode}") //通过 unicode 识别加入的用户(连接),放在一个集合中。
//每一个连接,就是一个实例。
public class RealTimeWebSocketServer{
    
    


    /**
     * 与某个客户端的连接对话,需要通过它来给客户端发送消息。
     * 每个用户(连接)私有。
     */
    private Session session;

    /**
     * static
     * 储存链接 <uniCode, set<instance>>
     * ConcurrentHashMap 线程安全,读取非阻塞
     * CopyOnWriteArraySet 线程安全
     */
    private static final ConcurrentHashMap<String, CopyOnWriteArraySet<RealTimeWebSocketServer>> webSocketSet = new ConcurrentHashMap<>();

	//每一个新连接进来执行的方法
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "uniCode") String uniCode) {
    
    
        this.session = session;
        //先查找是否有uniCode
        CopyOnWriteArraySet<RealTimeWebSocketServer> users = webSocketSet.get(uniCode);
        if (users == null) {
    
    
            //处理读并发
            synchronized (webSocketSet) {
    
    
                if (!webSocketSet.contains(uniCode)) {
    
    
                    users = new CopyOnWriteArraySet<>();
                    webSocketSet.put(uniCode, users);
                    generateRealTimeInfo(uniCode);
                }
            }
        }
        users.add(this);
        logger.info("连接成功,当前房间数为:" + webSocketSet.size()
                + ",连接ID:" + uniCode
                + ",房间号:" + uniCode
                + ",当前房间人数:" + webSocketSet.get(uniCode).size());
    }

	//关闭连接执行的方法
    @OnClose
    public void onClose(Session session) {
    
    
        // 避免多人同时在线直接关闭通道。
        Object[] objects = webSocketSet.get(this.uniCode).toArray();
        for (int i = 0; i < objects.length; i++) {
    
    
            if(((RealTimeWebSocketServer) objects[i]).session.equals(session)){
    
    
                //删除房间中当前用户
                webSocketSet.get(this.uniCode).remove((RealTimeWebSocketServer) objects[i]);
            }
        }
        if(webSocketSet.get(uniCode).size() <= 0){
    
    
            //删除房间
            webSocketSet.remove(uniCode);
            logger.info("ID:" + uniCode+ "退出成功 ");
        }else{
    
    
            logger.info("ID:" + uniCode+ " 1名用户退出,剩余" + webSocketSet.get(uniCode).size() + "名用户在线");
        }
    }

	//实例收到消息执行此方法
    @OnMessage
    public void onMessage(String message) {
    
    
        logger.info("收到消息:" + message);
        //刷新实时巡检进度
        CopyOnWriteArraySet<RealTimeWebSocketServer> users = webSocketSet.get(uniCode);
        if (users != null) {
    
    
            for (RealTimeWebSocketServer user : users) {
    
    
                    user.session.getAsyncRemote().sendText(message);
                    logger.info("发送消息:" + message);
            }
        }
    }

	//未知错误执行此方法
    @OnError
    public void onError(Session session, Throwable error) {
    
    
        logger.info("发生错误" + new Date());
        logger.error(error.getMessage(), error);
    }

}

4. Detección de latidos del corazón

¿Qué es la detección de latidos?

Generalmente, en una conexión larga, es posible que no haya intercambio de datos durante un largo período de tiempo, por lo que, en general, periódicamente, el front-end enviará un paquete de latidos al back-end para garantizar la validez de la conexión.

¿Por qué la detección de latidos no es enviada por el backend, sino por el frontend?

  • Puede haber una gran cantidad de conexiones en el backend. Si se envía un paquete de latidos a una conexión sin datos como una tarea programada, la sobrecarga será alta.
  • Desde un punto de vista empresarial, cada front-end es el emisor y receptor de la mayor parte de la información, y es más razonable controlar la conexión.
Implementación

La capa de aplicación, que es una función en la que los extremos frontal y posterior cooperan entre sí.

código de front-end

//心跳检测
var heartCheck = {
    
    
      timeout: 60000, //每隔60秒发送心跳
      severTimeout: 5000,  //服务端超时时间
      timeoutObj: null,
      serverTimeoutObj: null,
      start: function(){
    
    
        var _this = this;
        this.timeoutObj && clearTimeout(this.timeoutObj);
        this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
        this.timeoutObj = setTimeout(function(){
    
    
              //这里发送一个心跳,后端收到后,返回一个心跳消息,
              //onmessage拿到返回的心跳就说明连接正常
              ws.send("心跳包"); // 心跳包
              //计算答复的超时时间
              _this.serverTimeoutObj = setTimeout(function() {
    
    
                  ws.close();
              }, _this.severTimeout);
        }, this.timeout)
      }
}

====================================================================
  ws.onopen = function () {
    
    
    //心跳检测重置
    heartCheck.start();
  };
  
  ws.onmessage = function (event) {
    
    
    //拿到任何消息都说明当前连接是正常的
    console.log('接收到消息');
    heartCheck.start();
  }
  

Referencias

https://www.cnblogs.com/FatKee/articles/10250854.html

Supongo que te gusta

Origin blog.csdn.net/weixin_43742184/article/details/112555102
Recomendado
Clasificación