SseEmitterServer.java 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package com.cyksj.server;
  2. import lombok.extern.log4j.Log4j2;
  3. import org.springframework.http.MediaType;
  4. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  5. import java.io.IOException;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.concurrent.ConcurrentHashMap;
  10. import java.util.concurrent.atomic.AtomicInteger;
  11. import java.util.function.Consumer;
  12. @Log4j2
  13. public class SseEmitterServer {
  14. /**
  15. * 当前连接数
  16. */
  17. private static AtomicInteger count = new AtomicInteger(0);
  18. /**
  19. * 使用map对象,便于根据userId来获取对应的SseEmitter,或者放redis里面
  20. */
  21. private static Map<Long, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
  22. /**
  23. * 创建用户连接并返回 SseEmitter
  24. * @param userId 用户ID
  25. * @return SseEmitter
  26. */
  27. public static SseEmitter connect(Long userId) {
  28. SseEmitter sseEmitter = sseEmitterMap.get(userId);
  29. if (sseEmitter != null) {
  30. return sseEmitter;
  31. }
  32. // 设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException
  33. sseEmitter = new SseEmitter(0L);
  34. // 注册回调
  35. sseEmitter.onCompletion(completionCallBack(userId));
  36. sseEmitter.onError(errorCallBack(userId));
  37. sseEmitter.onTimeout(timeoutCallBack(userId));
  38. sseEmitterMap.put(userId, sseEmitter);
  39. // 数量+1
  40. count.getAndIncrement();
  41. log.info("创建新的sse连接,当前用户Id:{}", userId);
  42. return sseEmitter;
  43. }
  44. /**
  45. * 给指定用户发送信息
  46. */
  47. public static void sendMessage(Long userId, Object message) {
  48. if (sseEmitterMap.containsKey(userId)) {
  49. try {
  50. // sseEmitterMap.get(userId).send(message, MediaType.APPLICATION_JSON);
  51. sseEmitterMap.get(userId).send(message);
  52. } catch (IOException e) {
  53. log.error("用户[{}]推送异常:{}", userId, e.getMessage());
  54. removeUser(userId);
  55. }
  56. }
  57. }
  58. /**
  59. * 群发所有人
  60. */
  61. public static void batchSendMessage(String wsInfo) {
  62. sseEmitterMap.forEach((k, v) -> {
  63. try {
  64. v.send(wsInfo, MediaType.APPLICATION_JSON);
  65. } catch (IOException e) {
  66. log.error("用户[{}]推送异常:{}", k, e.getMessage());
  67. removeUser(k);
  68. }
  69. });
  70. }
  71. /**
  72. * 移除用户连接
  73. */
  74. public static void removeUser(Long userId) {
  75. sseEmitterMap.remove(userId);
  76. // 数量-1
  77. count.getAndDecrement();
  78. log.info("移除用户:{}", userId);
  79. }
  80. /**
  81. * 获取当前连接信息
  82. */
  83. public static List<Long> getIds() {
  84. return new ArrayList<>(sseEmitterMap.keySet());
  85. }
  86. /**
  87. * 获取当前连接数量
  88. */
  89. public static int getUserCount() {
  90. return count.intValue();
  91. }
  92. private static Runnable completionCallBack(Long userId) {
  93. return () -> {
  94. log.info("结束连接:{}", userId);
  95. removeUser(userId);
  96. };
  97. }
  98. private static Runnable timeoutCallBack(Long userId) {
  99. return () -> {
  100. log.info("连接超时:{}", userId);
  101. removeUser(userId);
  102. };
  103. }
  104. private static Consumer<Throwable> errorCallBack(Long userId) {
  105. return throwable -> {
  106. log.info("连接异常:{}", userId);
  107. removeUser(userId);
  108. };
  109. }
  110. }