191 lines
5.6 KiB
Java
191 lines
5.6 KiB
Java
package com.ruoyi.ws.service;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
|
|
import javax.websocket.Session;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import com.alibaba.fastjson2.JSON;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
/**
|
|
* WebSocket工具类
|
|
*/
|
|
@Service
|
|
@Slf4j
|
|
public class UserWebSocketService {
|
|
|
|
// 使用ConcurrentHashMap存储会话
|
|
private static final Map<Long, Session> SESSION_POOL = new ConcurrentHashMap<>();
|
|
|
|
// 创建线程池
|
|
private static final ExecutorService MESSAGE_EXECUTOR = Executors.newFixedThreadPool(10);
|
|
|
|
// 批处理大小
|
|
private static final int BATCH_SIZE = 100;
|
|
|
|
// 最大重试次数
|
|
private static final int MAX_RETRY = 3;
|
|
|
|
/**
|
|
* 添加用户WebSocket会话
|
|
*/
|
|
public void addSession(Long userId, Session session) {
|
|
if (userId != null && session != null) {
|
|
SESSION_POOL.put(userId, session);
|
|
log.info("用户{}的WebSocket会话已添加", userId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 移除用户WebSocket会话
|
|
*/
|
|
public void removeSession(Long userId) {
|
|
if (userId != null) {
|
|
SESSION_POOL.remove(userId);
|
|
log.info("用户{}的WebSocket会话已移除", userId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取用户WebSocket会话
|
|
*/
|
|
public Session getSession(Long userId) {
|
|
return userId != null ? SESSION_POOL.get(userId) : null;
|
|
}
|
|
|
|
/**
|
|
* 向指定用户发送消息
|
|
*
|
|
* @param message 消息内容
|
|
* @param userIds 目标用户ID数组
|
|
*/
|
|
public void sendMessageToUser(Object message, Long... userIds) {
|
|
if (message == null) {
|
|
return;
|
|
}
|
|
this.sendMessageToUser(message, Arrays.asList(userIds));
|
|
}
|
|
|
|
/**
|
|
* 向指定用户发送消息
|
|
*
|
|
* @param message 消息内容
|
|
* @param userIds 目标用户ID数组
|
|
*/
|
|
public void sendMessageToUser(Object message, Collection<Long> userIds) {
|
|
if (message == null) {
|
|
return;
|
|
}
|
|
|
|
String msg = JSON.toJSONString(message);
|
|
for (Long userId : userIds) {
|
|
Session session = this.getSession(userId);
|
|
if (session != null) {
|
|
try {
|
|
if (session.isOpen()) {
|
|
session.getBasicRemote().sendText(msg);
|
|
log.info("向用户{}发送消息成功: {}", userId, msg);
|
|
}
|
|
} catch (Exception e) {
|
|
log.error("发送消息给用户{}失败", userId, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 向所有在线用户发送消息
|
|
*
|
|
* @param message 消息内容
|
|
*/
|
|
public void sendMessageToAll(Object message) {
|
|
if (message == null) {
|
|
return;
|
|
}
|
|
|
|
String msg = JSON.toJSONString(message);
|
|
|
|
// 将在线用户分批处理
|
|
List<List<Map.Entry<Long, Session>>> batches = new ArrayList<>();
|
|
List<Map.Entry<Long, Session>> currentBatch = new ArrayList<>();
|
|
|
|
for (Map.Entry<Long, Session> entry : SESSION_POOL.entrySet()) {
|
|
currentBatch.add(entry);
|
|
if (currentBatch.size() >= BATCH_SIZE) {
|
|
batches.add(new ArrayList<>(currentBatch));
|
|
currentBatch.clear();
|
|
}
|
|
}
|
|
|
|
if (!currentBatch.isEmpty()) {
|
|
batches.add(currentBatch);
|
|
}
|
|
|
|
// 并行处理每个批次
|
|
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
|
|
|
for (List<Map.Entry<Long, Session>> batch : batches) {
|
|
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
|
processBatch(batch, msg);
|
|
}, MESSAGE_EXECUTOR);
|
|
|
|
futures.add(future);
|
|
}
|
|
|
|
// 等待所有批次处理完成
|
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
|
|
|
log.info("向所有在线用户发送消息完成: {}", msg);
|
|
}
|
|
|
|
/**
|
|
* 处理一个批次的消息发送
|
|
*/
|
|
private void processBatch(List<Map.Entry<Long, Session>> batch, String msg) {
|
|
for (Map.Entry<Long, Session> entry : batch) {
|
|
Long userId = entry.getKey();
|
|
Session session = entry.getValue();
|
|
|
|
if (session != null) {
|
|
sendMessageWithRetry(userId, session, msg, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 带重试机制的消息发送
|
|
*/
|
|
private void sendMessageWithRetry(Long userId, Session session, String msg, int retryCount) {
|
|
try {
|
|
synchronized (session) {
|
|
if (session.isOpen()) {
|
|
session.getBasicRemote().sendText(msg);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
if (retryCount < MAX_RETRY) {
|
|
log.warn("向用户{}发送消息失败,准备第{}次重试", userId, retryCount + 1);
|
|
try {
|
|
Thread.sleep(100 * (retryCount + 1)); // 递增延迟重试
|
|
sendMessageWithRetry(userId, session, msg, retryCount + 1);
|
|
} catch (InterruptedException ie) {
|
|
Thread.currentThread().interrupt();
|
|
}
|
|
} else {
|
|
log.error("向用户{}发送全局消息失败,已重试{}次", userId, MAX_RETRY, e);
|
|
}
|
|
}
|
|
}
|
|
}
|