SpringBoot整合websocket

添加依赖:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-websocketartifactId>
dependency>

开启WebSocket配置类

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

定时任务类:

@Configuration
@EnableScheduling
public class WebSocketScheduleTask {
    @Resource
    private SessionPool sessionPool;

    //清理超时Session
    @Scheduled(fixedRate = 60000) // 每分钟检查一次
    public void cleanupTimeoutSessions() {
        sessionPool.cleanupTimeoutSessions(300000); // 超时时间 5 分钟
    }
}

服务端:

@Slf4j
@Component
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketServer {

    //静态函数注入
    private final SessionPool sessionPool;

    public WebSocketServer(SessionPool sessionPool) {
        this.sessionPool = sessionPool;
    }
    public WebSocketServer() {
        // 通过 Spring 工具获取 Bean
        this.sessionPool = SpringUtil.getApplicationContext().getBean(SessionPool.class);;
    }


    //连接成功调用
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Long userId)  {
        sessionPool.addSession(userId, session);
    }

    //关闭连接
    @OnClose
    public void onClose(Session session, @PathParam("userId") Long userId)  {
        sessionPool.removeSession(userId);
        log.info("用户: {} 下线了", userId);
    }

    //收到客户端消息后使用的方法
    @OnMessage
    public void OnMessage(String message, Session session, @PathParam("userId") Long userId) {
        try {
            sessionPool.onMessage(message, userId, session); // 委托给 SessionPool 处理
            log.info("服务端收到用户userId:{} 的消息:{}", userId, message);
        } catch (Exception e) {
            log.error("消息处理异常, userId: {}, message: {}", userId, message, e);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket 错误", error);
    }
}

执行管理:

@Slf4j
@Component
public class SessionPool {
    public static final String PING = "ping";//心跳检测
    // 存储所有活跃的 Session 及其对应的锁对象
    private final ConcurrentHashMap<Long, Session> sessionMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Long, Object> sessionLocks = new ConcurrentHashMap<>();
    //用户的最后活跃时间
    private final ConcurrentHashMap<Long, Long> lastActiveTimeMap = new ConcurrentHashMap<>();

    //连接成功调用
    public void addSession(Long userId, Session session) {
        sessionMap.put(userId, session);
        sessionLocks.put(userId, new Object());
        log.info("新用户加入,连接成功!userId: {} 在线人数: {}", userId, sessionMap.size());
    }

    //关闭连接
    public void removeSession(Long userId) {
        Session session = sessionMap.remove(userId);
        if (StringUtils.isNotNull(session) && session.isOpen()) {
            try {
                session.close();
            } catch (IOException e) {
                log.error("关闭Session异常, userId: {},异常:{}", userId, e.getMessage());
            } finally {
                sessionLocks.remove(userId);
                lastActiveTimeMap.remove(userId);
            }
        }
    }

    //清理超时Session
    public void cleanupTimeoutSessions(long timeoutMillis) {
        long currentTime = System.currentTimeMillis();
        sessionMap.keySet().removeIf(userId -> {
            Long lastActiveTime = lastActiveTimeMap.get(userId);
            if (StringUtils.isNull(lastActiveTime) || (currentTime - lastActiveTime) > timeoutMillis) {
                Session session = sessionMap.get(userId);
                if ( StringUtils.isNotNull(session) && session.isOpen()) {
                    try {
                        session.close();
                    } catch (IOException e) {
                        log.error("清理超时Session异常, userId: {},异常:{}", userId, e.getMessage());
                    }
                }
                sessionLocks.remove(userId);
                lastActiveTimeMap.remove(userId);
                log.info("清理超时用户:{}", userId);
                return true;
            }
            return false;
        });
    }

    //处理消息
    public void onMessage(String message, Long userId, Session session) throws IOException {
        lastActiveTimeMap.put(userId, System.currentTimeMillis()); // 更新活跃时间
        Object sessionLock = sessionLocks.get(userId);
        if (StringUtils.isNull(sessionLock)) {
            log.warn("未找到userId: {} 的锁对象", userId);
            return;
        }
        synchronized (sessionLock) {
            if (message.equalsIgnoreCase(PING) && session.isOpen()) {
                // 心跳检测应答
                session.getBasicRemote().sendText(new JSONObject() {{
                    put("type", "pong");
                }}.toString());
                log.info("心跳检测,应答客户端:{}", userId);
            } else {
                JSONObject msgJson = JSON.parseObject(message);
                String type = msgJson.getString("type");
                // 调用业务逻辑
            }
        }
    }
}

你可能感兴趣的:(SpringBoot整合websocket)