springboot2.x establishes websocket server and client (front and back ends) and supports socket clusters

If you find it useful, please like + follow!

Websocket is a full-duplex communication protocol that allows the socket client and socket server to push data in both directions for interaction. Most of them build the socket server on the back end, and the front end acts as a socket client for access. But there will also be a situation where messages from within the backend need to be sent to the socket server and then pushed to other socket clients. In the implementation of springboot, there is no need to implement the process by itself. There is a well-encapsulated solution. . as follows:

1. Server construction

Need to introduce maven dependencies

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

Configure socket server bean object management

import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.websocket.server.ServerEndpointConfig;

@Log4j2
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator {

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

}

Server-side message processing

ICacheService uses the redis publish and subscribe function and is implemented using redission technology.

redis uses redisson to cache api template_Garc's blog-CSDN blog

import com.fusionfintrade.cache.ICacheService;
import com.fusionfintrade.config.WebSocketConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author Garcia
 * 集群原理:
 * 使用reids队列发布消息至各个集群节点
 * 各个节点监听redis对应的key消息,并将消息散发至各个节点对应的客户端中。
 */
@Component
@ServerEndpoint(value = "/ws/{from}", configurator = WebSocketConfig.class)
@Slf4j
public class SocketServer {

    @Resource
    private ICacheService cacheService;

    private static ConcurrentHashMap<String, CopyOnWriteArraySet<Session>> socketMap = new ConcurrentHashMap<>();

    private SocketServer socketServer;

    /**
     * 监听redis消息,并将对应的key消息使用socket发散至前端对应的功能
     */
    @PostConstruct
    private void init(){
        socketServer = this;
        cacheService.addListener(SocketClientEnum.RISK_WEB.getKey(), String.class,(channel, msg) ->socketServer.singleSendMessage(SocketClientEnum.RISK_WEB.getKey(),msg));
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("from") String from){
        //将session会话保存,根据来源存。
        CopyOnWriteArraySet<Session> sessionSet = socketMap.get(from);
        if (CollectionUtils.isEmpty(sessionSet)){
            sessionSet = new CopyOnWriteArraySet<>();
            sessionSet.add(session);
            socketMap.put(from,sessionSet);
        }else {
            sessionSet.add(session);
        }
        log.info("连接:{},[{}]集当前连接数:{}",session.getRequestURI(),from,sessionSet.size());
    }

    @OnMessage
    public void onMessage(String message,Session session){
        //接收其他客户端的socket消息,并发给指定客户端socket,key可以通过再message中携带进来
//        sendMessage(key,message);
    }

    @OnClose
    public void onClose(Session session, @PathParam("from") String from){
        CopyOnWriteArraySet<Session> sessions = socketMap.get(from);
        sessions.remove(session);
    }

    @OnError
    public void onError(Throwable e, @PathParam("from") String from){
        log.error("{}socket连接异常",from,e);
    }

    /**
     * 集群节点发布消息
     * key:对应功能的客户端
     * message:消息内容
     * @param key
     * @param message
     */
    public void clusterSendMessage(String key,String message){
        cacheService.publish(key,message);
    }

    /**
     * 单节点发布消息
     * key:对应功能的客户端
     * message:消息内容
     * @param message
     */
    public void singleSendMessage(String key,String message){
        try {
            CopyOnWriteArraySet<Session> session = socketMap.get(key);
            if (CollectionUtils.isEmpty(session)){
                return;
            }
            session.stream().parallel().forEach(s ->{
                try {
                    s.getBasicRemote().sendText(message);
                } catch (Exception e) {
                    log.error("websocket消息推送异常",e);
                }
            });
        }catch (Exception e){
            log.error("socket发送消息失败",e);
        }
    }
}

2. Back-end socket client construction

The client needs to create a connection before it can be used, but it only needs to be created once, so you can use spring to manage this object, or like me, write it in a singleton mode and extract it for use anytime and anywhere. The actual method used is sendMessage

import lombok.extern.log4j.Log4j2;

import javax.websocket.*;
import java.io.IOException;
import java.net.URI;

@Log4j2
@ClientEndpoint
public class SocketClient {

    private static SocketClient instance;

    private SocketClient(){}

    private Session session;

    public static synchronized SocketClient getInstance(){
        if (instance == null){
            instance = new SocketClient();
        }
        instance.create();
        return instance;
    }

    private void create(){
        if (session==null||!session.isOpen()){
            try {
                WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                container.connectToServer(instance,new URI("ws://127.0.0.1:18110/g-alarm/ws/system"));
            }catch (Exception e){
                log.error("socket client init error",e);
            }
        }
    }

    @OnOpen
    public void onOpen(Session session){
        this.session = session;
    }

    @OnMessage
    public void onMessage(String message,Session session){

    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason){

    }

    @OnError
    public void onError(Throwable e){
        log.error("socket连接异常",e);
    }

    public void sendMessage(String message){
        synchronized (session){
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("socket客户端发送消息异常");
            }
        }
    }
}

Guess you like

Origin blog.csdn.net/Qensq/article/details/125722899