国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

springBoot使用webSocket的幾種方式以及在高并發(fā)出現的問題及解決

這篇具有很好參考價值的文章主要介紹了springBoot使用webSocket的幾種方式以及在高并發(fā)出現的問題及解決。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、第一種方式-原生注解(tomcat內嵌)

1.1、引入依賴

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

1.2、配置文件

package cn.jt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月06日
 */
@Configuration
public class WebSocketConfig {

    /**
     * 初始化Bean,它會自動注冊使用了 @ServerEndpoint 注解聲明的 WebSocket endpoint
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

1.3、構建安全的WebSocket抽象層

1、該類可以作為一個基礎的安全抽象層,后續(xù)項目中如果需要做認證的操作,都可以繼承該抽象類

ClientUserInfoService 大家可以看作一個 UserService 就是一張用戶表的service類

這里認證采用的是 jwt的方式,大家可以換成自己的

2、大家這里注意,我們使用的是 javax.websocket.Session; 這個是tomcat下的
spring-boot-starter-websocket,spring boot,websocket,后端

package cn.jt.websocket;

import cn.jt.client.entity.ClientUserInfo;
import cn.jt.client.service.ClientUserInfoService;
import cn.jt.jwt.JwtUtils;
import cn.jt.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;

import javax.websocket.Session;
import java.io.IOException;
import java.util.Date;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月06日
 */
@Slf4j
public abstract class SecureWebSocket {
    private static final ClientUserInfoService clientUserInfoService;

    static {
        clientUserInfoService = SpringContextUtils.getBean(ClientUserInfoService.class);
    }

    protected Session session;

    protected String token;

    protected Long tokenExpiresAt;

    protected ClientUserInfo clientUserInfo;

    /**
     * 驗證token是否有效(包含有效期)
     *
     * @param token  token
     * @param isInit 是否對token和userInfo進行初始化賦值
     * @return boolean
     */
    protected boolean isTokenValid(String token, boolean isInit) {
        ClientUserInfo clientUserInfo;
        try {
            clientUserInfo = JwtUtils.getClientUserInfo(token);
        } catch (Exception e) {
            log.error("ws 認證失敗", e);
            return false;
        }
        if (isInit) {
            this.clientUserInfo = clientUserInfo;
            this.tokenExpiresAt = JwtUtils.getDecodedJWT(token).getExpiresAt().getTime();
            this.token = token;
        }
        return true;
    }

    /**
     * 認證失敗,斷開連接
     *
     * @param session session
     */
    protected void sendAuthFailed(Session session) {
        try {
            session.getBasicRemote().sendText("認證失敗");
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.4、構建基礎的WebSocket

1、代碼很簡單,大家一看就知道邏輯了,這里就解釋一下各個注解的含義

  • @ServerEndpoint:將目前的類定義成一個websocket服務器端,注解的值將被用于監(jiān)聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務器端
  • @OnOpen:當WebSocket建立連接成功后會觸發(fā)這個注解修飾的方法。
  • @OnClose:當WebSocket建立的連接斷開后會觸發(fā)這個注解修飾的方法。
  • @OnMessage:當客戶端發(fā)送消息到服務端時,會觸發(fā)這個注解修改的方法。
  • @OnError:當WebSocket建立連接時出現異常會觸發(fā)這個注解修飾的方法。

2、大家這里注意,我們使用的是 javax.websocket.Session; 這個是tomcat下的
spring-boot-starter-websocket,spring boot,websocket,后端

package cn.jt.websocket;

import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author GXM
 * @version 1.0.0
 * @Description 
 * @createTime 2023年07月06日
 */
@Slf4j
@ServerEndpoint("/globalWs/{token}")
@Component
public class GlobalWebsocket extends SecureWebSocket {

 
    /**
     * key: userKye
     * value: GlobalWebsocket  這里你直接存儲 session 也是可以的
     */
    private static final Map<String, GlobalWebsocket> CLIENTS = new ConcurrentHashMap<>();

    /**
     * // 如果允許 一個賬號 多人登錄的話  就 加上  "-" + tokenTime,因為每次登錄的token過期時間都是不一樣的
     * clientUserInfo.getId() + "-" + clientUserInfo.getAccount() ;
     */
    private String userKye;

    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        if (!isTokenValid(token, true)) {
            sendAuthFailed(session);
            return;
        }

        this.session = session;
        this.userKye = clientUserInfo.getId() + "-" + clientUserInfo.getAccount() + "-" + super.tokenExpiresAt;
        CLIENTS.put(userKye, this);
        log.info("當前在線用戶:{}", CLIENTS.keySet());

        try {
            session.getBasicRemote().sendText("連接成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public String onMessage(Session session, String message) {
        // 先判斷當前token 是否已經到期了
        if (!isTokenValid(token, false)) {
            sendAuthFailed(session);
            return null;
        }

        try {
            session.getBasicRemote().sendText("received");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
//        log.error("ws session 發(fā)生錯誤,session key is {}",throwable);
        log.error("ws session 發(fā)生錯誤:{}", throwable.getMessage());
    }

    @OnClose
    public void onClose(Session session) {
        CLIENTS.remove(userKye);
        log.info("ws 用戶 userKey {} 已下線,當前在線用戶:{}", userKye, CLIENTS.keySet());
    }

    /**
     * 發(fā)送消息
     *
     * @param messageVo
     */
    public void sendMessage(MessageVo messageVo) {
        try {
            this.session.getBasicRemote().sendText(JSON.toJSONString(messageVo));
        } catch (IOException e) {
            log.error("發(fā)送消息異常", e);
        }
    }

    /**
     * 向user精確用戶發(fā)送消息
     *
     * @param userKey   由 account + "-" + refreshToken的簽發(fā)時間組成,例:"admin-1635830649000"
     * @param messageVo 消息內容
     */
    public static void sendToUser(String userKey, MessageVo messageVo) {

        GlobalWebsocket globalWebsocket = CLIENTS.get(userKey);
        if (null != globalWebsocket) {
            globalWebsocket.sendMessage(messageVo);
            return;
        }
        log.error("發(fā)送消息到指定用戶,但是用戶不存在,userKey is {},message is {}", userKey, JSON.toJSONString(messageVo));
    }

    /**
     * 全體組播消息
     *
     * @param
     */
    public static void broadcast(MessageVo messageVo) {
        CLIENTS.values().forEach(c -> {
                    Session curSession = c.session;
                    if (curSession.isOpen()) {
                            try {
                                curSession.getBasicRemote().sendText(JSON.toJSONString(messageVo));
                            } catch (IOException e) {
                                log.error("發(fā)送ws數據錯誤:{}", e.getMessage());
                            }
                    }
                }
        );
    }
}

1.5、SpringBoot 開啟 WebSocket

@EnableWebSocket

spring-boot-starter-websocket,spring boot,websocket,后端

1.6、高并發(fā)時候的問題

1、這里要說明一下在高并發(fā)下的問題,如果你同時向在線的 3 個webSocket 在線客戶端發(fā)送消息,即廣播所有在線用戶(目前是3個),每個用戶每秒10條,那就是說,你每秒要發(fā)送 30 條數據,我們調用上述的廣播函數 broadcast(),有時候會出現

java.lang.IllegalStateException: 遠程 endpoint 處于 [xxxxxx] 狀態(tài),如:
The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for calle

這是因為在高并發(fā)的情況下,出現了session搶占的問題,導致session,的狀態(tài)不一致,所以,這里可以去嘗試加鎖操作,如下


 public static final ExecutorService WEBSOCKET_POOL_EXECUTOR = new ThreadPoolExecutor(
            20, 20,
            Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
            new ThreadFactoryBuilder()
                    .setNameFormat("GlobalWebsocket-executor-" + "%d")
                    .setUncaughtExceptionHandler((thread, throwable) -> log.error("ThreadPool {} got exception", thread, throwable)).build(),
            new ThreadPoolExecutor.AbortPolicy());
            
    /**
     * 全體組播消息
     *
     * @param
     */
    public static void broadcast(MessageVo messageVo) {
        CLIENTS.values().forEach(c -> {    
                    Session curSession = c.session;
                    if (curSession.isOpen()) {
                        // 建議單個session 一個線程,避免  一個session會話網絡不好,會出現超時異常,當前線程會因此中斷。
                        // 導致后面的session沒有進行發(fā)送操作。使用單個線程,單個session情況下避免session之間的相互影響。
                        WEBSOCKET_POOL_EXECUTOR.execute(() -> {
                            synchronized (curSession) {
                                // 雙重鎖檢查,外邊的 isOpen 第一遍過濾,里面枷加鎖之后,第二遍過濾
                                if (curSession.isOpen()) {
                                    try {
                                        curSession.getBasicRemote().sendText(JSON.toJSONString(messageVo));
                                    } catch (IOException e) {
                                        log.error("發(fā)送ws數據錯誤:{}", e.getMessage());
                                    }
                                }
                            }
                        });
                    }
                }
        );
    }

其中增加了,雙重鎖檢查,以及線程池操作,當然加上鎖之后,性能是肯定會有所下降的

建議單個session 一個線程,避免 一個session會話網絡不好,會出現超時異常,當前線程會因此中斷

2、按照上述的代碼,我這邊測試12個webSocket 鏈接,每秒每個客戶端都發(fā)送10條數據,相當于每秒發(fā)送120條數據,目前看來,速度還是不錯的,但是當客戶端重連后,偶爾會出現錯誤信息 遠程主機已經關閉了一個鏈接,類似于這種錯誤,這條錯誤日志是在廣播代碼的如下位置打印的,這是因為當準備發(fā)送消息的時候,遠程客戶端還是關閉了。

 try {
                                        curSession.getBasicRemote().sendText(JSON.toJSONString(messageVo));
                                    } catch (IOException e) {
                                        log.error("發(fā)送ws數據錯誤:{}", e.getMessage());
                                    }

二、第二種方式-Spring封裝

2.1、引入依賴

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

2.2、自己的webSocket處理service

1、WebSocketService 處理器類如下

類似于 UserService 等等,主要是抽出一部分的業(yè)務邏輯

package cn.jt.websocket.spring;

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月19日
 */
public interface WebSocketService {
    /**
     * 會話開始回調
     *
     * @param session 會話
     */
    void handleOpen(WebSocketSession session);

    /**
     * 會話結束回調
     *
     * @param session 會話
     */
    void handleClose(WebSocketSession session);

    /**
     * 處理消息
     *
     * @param session 會話
     * @param message 接收的消息
     */
    void handleMessage(WebSocketSession session, String message);

    /**
     * 發(fā)送消息
     *
     * @param session 當前會話
     * @param message 要發(fā)送的消息
     * @throws IOException 發(fā)送io異常
     */
    void sendMessage(WebSocketSession session, String message) throws IOException;

    /**
     * 發(fā)送消息
     *
     * @param userId  用戶id
     * @param message 要發(fā)送的消息
     * @throws IOException 發(fā)送io異常
     */
    void sendMessage(Integer userId, TextMessage message) throws IOException;

    /**
     * 發(fā)送消息
     *
     * @param userId  用戶id
     * @param message 要發(fā)送的消息
     * @throws IOException 發(fā)送io異常
     */
    void sendMessage(Integer userId, String message) throws IOException;

    /**
     * 發(fā)送消息
     *
     * @param session 當前會話
     * @param message 要發(fā)送的消息
     * @throws IOException 發(fā)送io異常
     */
    void sendMessage(WebSocketSession session, TextMessage message) throws IOException;

    /**
     * 廣播
     *
     * @param message 字符串消息
     * @throws IOException 異常
     */
    void broadCast(String message) throws IOException;

    /**
     * 廣播
     *
     * @param message 文本消息
     * @throws IOException 異常
     */
    void broadCast(TextMessage message) throws IOException;

    /**
     * 處理會話異常
     *
     * @param session 會話
     * @param error   異常
     */
    void handleError(WebSocketSession session, Throwable error);
}

2、WebSocketServiceImpl 實現類如下

類似于 UserServiceImpl 等等,主要是抽出一部分的業(yè)務邏輯


package cn.jt.websocket.spring;

import cn.jt.client.entity.ClientUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月19日
 */
@Slf4j
public class WebSocketServiceImpl implements WebSocketService {

    private final Map<Integer, WebSocketSession> clients = new ConcurrentHashMap<>();

    @Override
    public void handleOpen(WebSocketSession session) {
        // 這個時候就需要在建立 webSocket 時存儲的 用戶信息了
        Map<String, Object> attributes = session.getAttributes();
        ClientUserInfo clientUserInfo = (ClientUserInfo) attributes.get("clientUserInfo");
        clients.put(clientUserInfo.getId(), session);

        log.info("a new connection opened,current online count:{}", clients.size());
    }

    @Override
    public void handleClose(WebSocketSession session) {
        // 這個時候就需要在建立 webSocket 時存儲的 用戶信息了
        Map<String, Object> attributes = session.getAttributes();
        ClientUserInfo clientUserInfo = (ClientUserInfo) attributes.get("clientUserInfo");
        clients.remove(clientUserInfo.getId());
        log.info("a new connection closed,current online count:{}", clients.size());
    }

    @Override
    public void handleMessage(WebSocketSession session, String message) {
        // 只處理前端傳來的文本消息,并且直接丟棄了客戶端傳來的消息
        log.info("received a message:{}", message);
    }

    @Override
    public void sendMessage(WebSocketSession session, String message) throws IOException {
        this.sendMessage(session, new TextMessage(message));
    }

    @Override
    public void sendMessage(Integer userId, TextMessage message) throws IOException {
        WebSocketSession webSocketSession = clients.get(userId);

        if (webSocketSession.isOpen()) {
            webSocketSession.sendMessage(message);
        }
    }

    @Override
    public void sendMessage(Integer userId, String message) throws IOException {
        this.sendMessage(userId, new TextMessage(message));
    }

    @Override
    public void sendMessage(WebSocketSession session, TextMessage message) throws IOException {
        session.sendMessage(message);
    }

    @Override
    public void broadCast(String message) throws IOException {
        clients.values().forEach(session -> {
            if (session.isOpen()) {
                try {
                    session.sendMessage(new TextMessage(message));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    @Override
    public void broadCast(TextMessage message) throws IOException {
        clients.values().forEach(session -> {
            if (session.isOpen()) {
                try {
                    session.sendMessage(message);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    @Override
    public void handleError(WebSocketSession session, Throwable error) {
        log.error("websocket error:{},session id:{}", error.getMessage(), session.getId());
        log.error("", error);
    }

}

2.3、實現spring框架的WebSocket處理器

1、注意這里的 webSocketSession 就是 spring 包下的了,不再是 tomcat包下的了

spring-boot-starter-websocket,spring boot,websocket,后端

這里其實就和我們之前使用原生注解(tomcat)的那個一樣了,都是幾個特定的函數

我們在特定的方法下,調用我們自己的 service去單獨處理,解耦合

package cn.jt.websocket.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.*;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月19日
 */
public class DefaultWebSocketHandler implements WebSocketHandler {

    @Autowired
    private WebSocketService webSocketService;
    /**
     * 建立連接
     *
     * @param session Session
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        webSocketService.handleOpen(session);
    }

    /**
     * 接收消息
     *
     * @param session Session
     * @param message 消息
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;
            webSocketService.handleMessage(session, textMessage.getPayload());
        }
    }

    /**
     * 發(fā)生錯誤
     *
     * @param session   Session
     * @param exception 異常
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        webSocketService.handleError(session, exception);
    }

    /**
     * 關閉連接
     *
     * @param session     Session
     * @param closeStatus 關閉狀態(tài)
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {
        webSocketService.handleClose(session);
    }

    /**
     * 是否支持發(fā)送部分消息
     *
     * @return false
     */
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

2.4、自定義攔截器

這里,我們可以設置攔截器,在做請求參數,或者權限認證的時候,不用在建立鏈接的函數afterConnectionEstablished里面去處理

可以理解為 springMvc 每次請求前的攔截器

package cn.jt.websocket.spring;

import cn.jt.client.entity.ClientUserInfo;
import cn.jt.jwt.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月19日
 */
@Slf4j
public class WebSocketInterceptor implements HandshakeInterceptor {

    /**
     * 建立請求之前,可以用來做權限判斷
     *
     * @param request    the current request
     * @param response   the current response
     * @param wsHandler  the target WebSocket handler
     * @param attributes the attributes from the HTTP handshake to associate with the WebSocket
     *                   session; the provided attributes are copied, the original map is not used.
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request,
                                   ServerHttpResponse response, WebSocketHandler wsHandler,
                                   Map<String, Object> attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
            // 模擬用戶(通常利用JWT令牌解析用戶信息)
            String token = servletServerHttpRequest.getServletRequest().getParameter("token");
            try {
                ClientUserInfo clientUserInfo = JwtUtils.getClientUserInfo(token);
                // 設置當前這個session的屬性,后續(xù)我們在發(fā)送消息時,可以通過 session.getAttributes().get("clientUserInfo")可以取出 clientUserInfo參數
                attributes.put("clientUserInfo", clientUserInfo);
            } catch (Exception e) {
                log.error("webSocket 認證失敗 ", e);
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * 建立請求之后
     *
     * @param request   the current request
     * @param response  the current response
     * @param wsHandler the target WebSocket handler
     * @param exception an exception raised during the handshake, or {@code null} if none
     */
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
                               WebSocketHandler wsHandler, Exception exception) {

    }
}


2.5、WebSocket配置

將自定義處理器、攔截器以及WebSocket操作類依次注入到IOC容器中。

  • @EnableWebSocket:開啟WebSocket功能
  • addHandler:添加處理器
  • addInterceptors:添加攔截器
  • setAllowedOrigins:設置允許跨域(允許所有請求來源)
package cn.jt.websocket.spring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * @author GXM
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年07月19日
 */
@Configuration
public class WebSocketConfiguration implements WebSocketConfigurer {
    @Bean
    public DefaultWebSocketHandler defaultWebSocketHandler() {
        return new DefaultWebSocketHandler();
    }

    @Bean
    public WebSocketService webSocket() {
        return new WebSocketServiceImpl();
    }

    @Bean
    public WebSocketInterceptor webSocketInterceptor() {
        return new WebSocketInterceptor();
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 鏈接方式如下 ws://127.0.0.1:9085/globalWs/message?token=qwncjncwqqdnjncz.adqwascsvcgrgb.cbrtbfvb
        // 如果你設置了springboot的 contentPath 那就需要在:9085端口后面 加上contentPath 的值,在拼接上  globalWs/message?token=qwncjncwqqdnjncz.adqwascsvcgrgb.cbrtbfvb
        registry.addHandler(defaultWebSocketHandler(), "/globalWs/message")
                .addInterceptors(webSocketInterceptor())
                .setAllowedOrigins("*");
    }
}

2.6、SpringBoot 開啟 WebSocket

@EnableWebSocket

spring-boot-starter-websocket,spring boot,websocket,后端

2.7、鏈接

1、其中 thermal-api 是我的項目名稱

spring-boot-starter-websocket,spring boot,websocket,后端

2、鏈接路徑如下

ws://127.0.0.1:9085/thermal-api/globalWs/message?token=qwncjncwqqdnjncz.adqwascsvcgrgb.cbrtbfvb

2.8、高并發(fā)時候的問題

1、如果在廣播的時候,客戶端很多,發(fā)送的消息也是很多,還是會出現和之前 第一種方式-原生注解(tomcat內嵌)相同的問題,出現類似如下報錯

The remote endpoint was in state [xxxx] which is an invalid state for calle

2、錯誤分析可以看 踩坑筆記 Spring websocket并發(fā)發(fā)送消息異常,寫的很清楚。

2.8.1、解決方案一

1、和之前一樣,加鎖

@Override
    public void broadCast(String message) throws IOException {
        clients.values().forEach(session -> {
            if (session.isOpen()) {
                synchronized (session){
                    try {
                        session.sendMessage(new TextMessage(message));
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
    }

2.8.2、解決方案二

1、使用 spring 的,Spring 的解決方案是把原來的 WebSocketSession 封了一層,即 org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator

3、代碼稍微改一下,如下

@Override
    public void handleOpen(WebSocketSession session) {
        // 這個時候就需要在建立 webSocket 時存儲的 用戶信息了
        Map<String, Object> attributes = session.getAttributes();
        ClientUserInfo clientUserInfo = (ClientUserInfo) attributes.get("clientUserInfo");
        
        clients.put(clientUserInfo.getId(), new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 64000));
        log.info("a new connection opened,current online count:{}", clients.size());
    }

第三種方式-TIO

1、請上網了解,用的比較少,不做過多說明文章來源地址http://www.zghlxwxcb.cn/news/detail-702422.html

第四種方式-STOMP

1、請上網了解,用的比較少,不做過多說明

到了這里,關于springBoot使用webSocket的幾種方式以及在高并發(fā)出現的問題及解決的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 玩轉SpringBoot:SpringBoot的幾種定時任務實現方式

    在現代軟件開發(fā)中,定時任務是一種常見的需求,用于執(zhí)行周期性的任務或在特定的時間點執(zhí)行任務。這些任務可能涉及數據同步、數據備份、報表生成、緩存刷新等方面,對系統(tǒng)的穩(wěn)定性和可靠性有著重要的影響。 Spring Boot 提供了強大且簡單的定時任務功能,使開發(fā)人員能

    2024年03月09日
    瀏覽(28)
  • SpringBoot實現異步調用的幾種方式

    SpringBoot實現異步調用的幾種方式

    一、使用 CompletableFuture 實現異步任務 CompletableFuture 是 Java 8 新增的一個異步編程工具,它可以方便地實現異步任務。使用 CompletableFuture 需要滿足以下條件: 異步任務的返回值類型必須是 CompletableFuture 類型; 在異步任務中使用 CompletableFuture.supplyAsync() 或 CompletableFuture.runAsy

    2024年02月08日
    瀏覽(23)
  • springboot接收前端參數的幾種方式

    springboot接收前端參數的幾種方式

    目錄 第一種:直接在方法中指定參數 第二種:使用@requesrParam注解 第三種方法:基于@pathVariable? 第四種方法:基于@ResquestBody 在開始之前,我們需要一下準備工作,創(chuàng)建數據庫,springboot工程,添加依賴,配置文件,使用的技術有mybatisplus,springboot,maven,mysql。 首先,數據庫

    2024年02月07日
    瀏覽(23)
  • SpringBoot 中實現定時任務的幾種方式

    SpringBoot 中實現定時任務的幾種方式

    定時任務在我們項目開發(fā)中也是很重要的,對于某些場景必須要用定時任務 ,如定時發(fā)送郵件啊,定時統(tǒng)計數據等,這篇文章主要講講項目中實現定時任務的幾種方式。 這種方式很簡單,主要就是先@EnableScheduling開啟定時任務功能,然后在相應的方法上添加@Scheduled()中間寫上

    2024年02月03日
    瀏覽(23)
  • 【SpringBoot系列】實現跨域的幾種方式

    【SpringBoot系列】實現跨域的幾種方式

    前言 在Web開發(fā)中,跨域是一個常見的問題。由于瀏覽器的同源策略,一個Web應用程序只能訪問與其自身同源(即,相同協(xié)議、主機和端口)的資源。 這種策略的存在是為了保護用戶的安全,防止惡意網站讀取或修改用戶的數據。 然而,現代Web應用程序經常需要訪問不同源的

    2024年02月01日
    瀏覽(18)
  • 【SpringBoot系列】接收前端參數的幾種方式

    【SpringBoot系列】接收前端參數的幾種方式

    前言 在現代Web開發(fā)中,前后端分離的架構已經成為主流。前端負責展示頁面和用戶交互,而后端則負責處理業(yè)務邏輯和數據存儲。在這種架構下,前端需要將用戶輸入的數據發(fā)送給后端進行處理。而Spring Boot作為一種快速開發(fā)框架,提供了多種方式來接收前端數據。 本文將介

    2024年02月05日
    瀏覽(24)
  • uniapp頁面跳轉的幾種方式 以及舉例(2)

    uniapp頁面跳轉的幾種方式 以及舉例(2)

    又來混時長 嫖流量卷了 保留當前頁面,跳轉到應用內的某個頁面,使用 uni.navigateBack 可以返回到原頁面。 ? 關閉當前頁面,跳轉到應用內的某個頁面。 ? 關閉所有頁面,打開到應用內的某個頁面。 跳轉到 tabBar 頁面,并關閉其他所有非 tabBar 頁面。 關閉當前頁面,返回上一

    2024年01月19日
    瀏覽(83)
  • SpringBoot 啟動項目后執(zhí)行方法的幾種方式

    在項目開發(fā)中某些場景必須要用到啟動項目后立即執(zhí)行方式的功能,如我們需要去初始化數據到 redis 緩存、設置策略工廠,或者啟動后讀取相應的配置等,主要聊聊實現立即執(zhí)行的幾種方法。 這兩者的實現方法一樣,都是去繼承相應的接口然后重寫 run 方法即可,也都是 S

    2024年02月11日
    瀏覽(19)
  • SpringBoot中接收POST參數的幾種方式

    SpringBoot中接收POST參數的幾種方式

    今天在做一個vue前后端分離項目的過程中,踩了一個坑,記錄一下 前端如下: 用戶名字段: username 密碼字段: password 提交后,發(fā)現后端怎么也收不到參數,總結如下: 常見的接收post參數,有三種 額外參數: 使用 required = false 標注參數是非必須的。 使用 defaultValue 給參數

    2024年02月15日
    瀏覽(31)
  • 【SpringBoot系列】讀取yml文件的幾種方式

    【SpringBoot系列】讀取yml文件的幾種方式

    前言 在Spring Boot開發(fā)中,配置文件是非常重要的一部分,而yml文件作為一種常用的配置文件格式,被廣泛應用于Spring Boot項目中。Spring Boot提供了多種方式來讀取yml文件中的屬性值,開發(fā)者可以根據具體的需求和場景選擇合適的方式。本文將介紹Spring Boot讀取yml文件的主要方式

    2024年02月05日
    瀏覽(17)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包