Web消息推送之SSE

时间:2025-04-23 18:21:52
@RestController @CrossOrigin @RequestMapping("/sse") public class SseEmitterServer { private static final Logger logger = LoggerFactory.getLogger(SseEmitterServer.class); /** * 当前连接数,不准确,如果用户多次刷新会导致偏少 */ private static AtomicInteger count = new AtomicInteger(0); /** * 使用map对象,便于根据userId来获取对应的SseEmitter,或者放redis里面 */ private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>(); /** * 创建用户连接并返回 SseEmitter * * @param userId 用户ID * @return SseEmitter */ @GetMapping(path = "/subscribe/{userId}", produces = {MediaType.TEXT_EVENT_STREAM_VALUE}) public SseEmitter subscribe(@PathVariable("userId") String userId) throws IOException { // 设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException SseEmitter sseEmitter = new SseEmitter(30_000L); // 设置前端的重试时间为15s,如果不加这个发送一下,前端就不会显示连接成功 sseEmitter.send("连接成功"); // 注册回调 sseEmitter.onCompletion(completionCallBack(userId)); sseEmitter.onError(errorCallBack(userId)); sseEmitter.onTimeout(timeoutCallBack(userId)); sseEmitterMap.put(userId, sseEmitter); // 数量+1 count.getAndIncrement(); logger.info("创建新的sse连接,当前用户:{}", userId); return sseEmitter; } /** * 给指定用户发送信息 -- 单播 */ @GetMapping(path = "sendMessage") public void sendMessage(String userId, String message) { if (sseEmitterMap.containsKey(userId)) { try { // (userId).send(message, MediaType.APPLICATION_JSON); System.out.println(userId + "==" + message); sseEmitterMap.get(userId).send(message); } catch (IOException e) { logger.error("用户[{}]推送异常:{}", userId, e.getMessage()); removeUser(userId); } } } /** * 向多人发布消息 -- 组播 * @param groupId 开头标识 * @param message 消息内容 */ public void groupSendMessage(String groupId, String message) { if (MapUtils.isNotEmpty(sseEmitterMap)) { /*Set<String> ids = ().stream().filter(m -> (groupId)).collect(()); batchSendMessage(message, ids);*/ sseEmitterMap.forEach((k, v) -> { try { if (k.startsWith(groupId)) { v.send(message, MediaType.APPLICATION_JSON); } } catch (IOException e) { logger.error("用户[{}]推送异常:{}", k, e.getMessage()); removeUser(k); } }); } } /** * 群发所有人 -- 广播 */ public void batchSendMessage(String message) { sseEmitterMap.forEach((k, v) -> { try { v.send(message, MediaType.APPLICATION_JSON); } catch (IOException e) { logger.error("用户[{}]推送异常:{}", k, e.getMessage()); removeUser(k); } }); } /** * 群发消息 */ public void batchSendMessage(String message, Set<String> ids) { ids.forEach(userId -> sendMessage(userId, message)); } /** * 移除用户连接 */ @GetMapping("/close/{userId}") public void removeUser(@PathVariable("userId") String userId) { sseEmitterMap.remove(userId); // 数量-1 count.getAndDecrement(); logger.info("移除用户:{}", userId); } /** * 获取当前连接信息 */ public List<String> getIds() { return new ArrayList<>(sseEmitterMap.keySet()); } /** * 获取当前连接数量 */ @GetMapping("/getUserCount") public int getUserCount() { return count.intValue(); } private Runnable completionCallBack(String userId) { return () -> { logger.info("结束连接:{}", userId); // removeUser(userId); }; } private Runnable timeoutCallBack(String userId) { return () -> { logger.info("连接超时:{}", userId); removeUser(userId); }; } private Consumer<Throwable> errorCallBack(String userId) { return throwable -> { logger.info("连接异常:{}", userId); removeUser(userId); }; } }