From 0fadda681984e8838838cb97c3f9a4d22793f30f Mon Sep 17 00:00:00 2001 From: liu Date: Wed, 20 Dec 2023 16:21:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4websocket=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../supervision/config/JwtInterceptor.java | 3 - .../constant/UserTokenConstant.java | 4 +- .../supervision/config/WebSocketConfig.java | 1 + .../controller/UserController.java | 44 ++++++++++--- .../controller/WebSocketServer.java | 45 ++++++-------- .../service/impl/DiagnoseHallServiceImpl.java | 4 +- .../usermanage/UserWebSocketDTO.java | 17 ----- .../KickUserListener.java | 10 ++- .../RedisListener.java} | 4 +- .../UserResourceCheck.java | 62 +++++++------------ .../websocket/WebSocketSessionPool.java | 13 ++++ .../WebsocketKeepaliveTask.java | 23 ++----- .../websocket/dto/KeepaliveDTO.java | 12 ++++ .../supervision/websocket/dto/KickDTO.java | 12 ++++ .../websocket/dto/NoResourceDTO.java | 10 +++ .../websocket/dto/UserWebSocketDTO.java | 12 ++++ .../websocket/dto/WebSocketLoginDTO.java | 28 +++++++++ 17 files changed, 183 insertions(+), 121 deletions(-) delete mode 100644 virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java rename virtual-patient-web/src/main/java/com/supervision/{usermanage => websocket}/KickUserListener.java (75%) rename virtual-patient-web/src/main/java/com/supervision/{usermanage/RedisConfig.java => websocket/RedisListener.java} (93%) rename virtual-patient-web/src/main/java/com/supervision/{usermanage => websocket}/UserResourceCheck.java (52%) create mode 100644 virtual-patient-web/src/main/java/com/supervision/websocket/WebSocketSessionPool.java rename virtual-patient-web/src/main/java/com/supervision/{task => websocket}/WebsocketKeepaliveTask.java (53%) create mode 100644 virtual-patient-web/src/main/java/com/supervision/websocket/dto/KeepaliveDTO.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/websocket/dto/KickDTO.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/websocket/dto/NoResourceDTO.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/websocket/dto/UserWebSocketDTO.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/websocket/dto/WebSocketLoginDTO.java diff --git a/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java b/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java index 6dfb5735..70fe2534 100644 --- a/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java +++ b/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java @@ -48,12 +48,9 @@ public class JwtInterceptor implements HandlerInterceptor { throw new BusinessException("当前用户未登录", HttpStatus.UNAUTHORIZED.value()); } } - JWT jwt = JWTUtil.parseToken(token); // 校验token是否过期,如果过期了,需要提示过期重新登录 checkTokenExpire(jwt); - // 校验是否重复登录 - //checkSingleLogin(jwt); cacheAuth(jwt); return true; } diff --git a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java index edf4a2b3..0b1caaa6 100644 --- a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java +++ b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java @@ -7,6 +7,8 @@ public interface UserTokenConstant { Integer KICK_CODE = 10000; - Integer KEEPALIVE_CODE = 10001; + Integer NO_RESOURCE_CODE = 10001; + + Integer KEEPALIVE_CODE = 10002; } diff --git a/virtual-patient-web/src/main/java/com/supervision/config/WebSocketConfig.java b/virtual-patient-web/src/main/java/com/supervision/config/WebSocketConfig.java index e2755bed..b2aab455 100644 --- a/virtual-patient-web/src/main/java/com/supervision/config/WebSocketConfig.java +++ b/virtual-patient-web/src/main/java/com/supervision/config/WebSocketConfig.java @@ -25,4 +25,5 @@ public class WebSocketConfig { threadPoolTaskScheduler.setRemoveOnCancelPolicy(true); return threadPoolTaskScheduler; } + } diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java b/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java index dcfee9ed..4de37f6c 100644 --- a/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java +++ b/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java @@ -2,7 +2,9 @@ package com.supervision.controller; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObjectIter; import cn.hutool.json.JSONUtil; import com.supervision.constant.UserTokenConstant; import com.supervision.exception.BusinessException; @@ -13,21 +15,23 @@ import com.supervision.pojo.vo.UserInfoReqVo; import com.supervision.pojo.vo.UserInfoResVo; import com.supervision.service.UserManageService; import com.supervision.service.UserService; -import com.supervision.usermanage.UserWebSocketDTO; -import com.supervision.usermanage.UserResourceCheck; +import com.supervision.websocket.dto.UserWebSocketDTO; +import com.supervision.websocket.UserResourceCheck; import com.supervision.util.TokenUtil; import com.supervision.util.UserUtil; +import com.supervision.websocket.dto.WebSocketLoginDTO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; +import org.apache.xmlbeans.impl.common.IOUtil; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; import org.springframework.web.bind.annotation.*; +import java.io.IOException; import java.time.LocalDateTime; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -78,8 +82,32 @@ public class UserController { @ApiOperation("踢用户下线") @GetMapping("kickUser") - public void kickUser(String userId) { - redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(userId, null))); + public void kickUser(String uuid, String userId) throws IOException { + if (StrUtil.isNotBlank(uuid)) { + redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(uuid))); + } else { + ScanOptions options = ScanOptions.scanOptions() + .match("*" + userId + "*") // 可选:使用模式匹配指定要匹配的键 + .count(10) // 可选:指定每次迭代返回的元素数量 + .build(); + + Cursor> cursor = null; + try { + cursor = redisTemplate.opsForHash().scan(UserTokenConstant.USER_WEBSOCKET_CACHE, options); + while (cursor.hasNext()) { + Map.Entry entry = cursor.next(); + Object value = entry.getValue(); + WebSocketLoginDTO bean = JSONUtil.toBean(String.valueOf(value), WebSocketLoginDTO.class); + if (userId.equals(bean.getUserId())) { + redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(bean.getUserId()))); + } + } + } catch (Exception e) { + if (ObjectUtil.isNotNull(cursor)) { + cursor.close(); + } + } + } } @ApiOperation("查看资源是否有剩余") @@ -122,7 +150,7 @@ public class UserController { @ApiOperation("获取当前在线的用户") @GetMapping("queryCurrentOnlineUser") - public Map queryCurrentOnlineUser(){ + public Map queryCurrentOnlineUser() { return redisTemplate.opsForHash().entries(UserTokenConstant.USER_WEBSOCKET_CACHE); } diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java b/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java index 75f90b05..37d274d0 100644 --- a/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java +++ b/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java @@ -1,39 +1,33 @@ package com.supervision.controller; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import com.supervision.constant.UserTokenConstant; -import com.supervision.usermanage.UserResourceCheck; -import com.supervision.util.SpringBeanUtil; +import com.supervision.websocket.UserResourceCheck; +import com.supervision.websocket.WebSocketSessionPool; +import com.supervision.websocket.dto.WebSocketLoginDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; 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.concurrent.ConcurrentHashMap; @Component @Slf4j -@ServerEndpoint("/webSocket/{uid}") +@ServerEndpoint("/webSocket/{uuid}/{userId}") public class WebSocketServer { - private static RedisTemplate redisTemplate ; + private static RedisTemplate redisTemplate; - //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。 - public static final ConcurrentHashMap SESSION_POOL = new ConcurrentHashMap<>(); - - private static UserResourceCheck userResourceCheck ; + private static UserResourceCheck userResourceCheck; // 因为是ServerEndpoint是多例,所以需要这样注入 @Autowired public void setUserResourceCheck(UserResourceCheck userResourceCheck) { WebSocketServer.userResourceCheck = userResourceCheck; } + // 因为是ServerEndpoint是多例,所以需要这样注入 @Autowired public void setRedisTemplate(RedisTemplate redisTemplate) { @@ -44,30 +38,31 @@ public class WebSocketServer { * 有客户端连接成功 */ @OnOpen - public void onOpen(Session session, @PathParam(value = "uid") String uid) { - userResourceCheck.achieveDiagnoseResourceAndOpenConnection(uid, session); - SESSION_POOL.put(uid, session); - log.info("用户:{}登录成功", uid); + public void onOpen(Session session, @PathParam(value = "uuid") String uuid, @PathParam(value = "userId") String userId) { + // 校验资源数量,并保存 + userResourceCheck.achieveDiagnoseResourceAndOpenConnection(uuid, userId, session); + WebSocketSessionPool.SESSION_POOL.put(uuid, session); + log.info("用户:{},UUID:{}登录成功", userId, uuid); } /** * 连接关闭调用的方法 */ @OnClose - public void onClose(Session session, @PathParam(value = "uid") String uid) { - redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uid, session.getId()); - SESSION_POOL.remove(uid); - log.info("用户:{}关闭,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE)); + public void onClose(Session session, @PathParam(value = "uuid") String uuid, @PathParam(value = "userId") String userId) { + redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uuid); + WebSocketSessionPool.SESSION_POOL.remove(uuid); + log.info("用户:{},uuid:{}关闭,从Redis中移除,当前连接数为:{}", userId, uuid, redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE)); } /** * 发生错误 */ @OnError - public void onError(Session session, @PathParam(value = "uid") String uid, Throwable throwable) { - redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uid, session.getId()); - SESSION_POOL.remove(uid); - log.error("用户:{}发生错误,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE), throwable); + public void onError(Session session, @PathParam(value = "uuid") String uuid, @PathParam(value = "userId") String userId, Throwable throwable) { + redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uuid); + WebSocketSessionPool.SESSION_POOL.remove(uuid); + log.error("用户:{},uuid:{}发生错误,从Redis中移除,当前连接数为:{}", userId, uuid, redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE), throwable); } diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java index fb2b5418..1899b1d7 100644 --- a/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java +++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java @@ -1,8 +1,6 @@ package com.supervision.service.impl; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Opt; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.lock.annotation.Lock4j; @@ -15,7 +13,7 @@ import com.supervision.vo.ask.DiagnosisPrimaryVO; import com.supervision.vo.manage.MedicalRecPageResVO; import com.supervision.vo.result.DiagnoseProcessReqVo; import com.supervision.vo.result.DiagnoseProcessResVo; -import com.supervision.usermanage.UserResourceCheck; +import com.supervision.websocket.UserResourceCheck; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java deleted file mode 100644 index 063401f1..00000000 --- a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.supervision.usermanage; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class UserWebSocketDTO { - - private String userId; - - /** - * 忽略提除的ID - */ - private String ignoreSessionId; - -} diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java b/virtual-patient-web/src/main/java/com/supervision/websocket/KickUserListener.java similarity index 75% rename from virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java rename to virtual-patient-web/src/main/java/com/supervision/websocket/KickUserListener.java index 3a7a54d9..b24841d1 100644 --- a/virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/KickUserListener.java @@ -1,16 +1,14 @@ -package com.supervision.usermanage; +package com.supervision.websocket; import cn.hutool.json.JSONUtil; import com.supervision.constant.UserTokenConstant; -import com.supervision.controller.WebSocketServer; +import com.supervision.websocket.dto.UserWebSocketDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.stereotype.Component; -import java.io.IOException; - @Slf4j @Component @@ -23,7 +21,7 @@ public class KickUserListener implements MessageListener { public void onMessage(Message message, byte[] pattern) { String messageString = message.toString(); UserWebSocketDTO user = JSONUtil.toBean(messageString, UserWebSocketDTO.class); - log.info("Redis的Channel:{}收到踢用户{}下线消息", UserTokenConstant.KICK_CHANNEL, user.getUserId()); - userResourceCheck.kickUser(user.getUserId(), user.getIgnoreSessionId()); + log.info("Redis的Channel:{}收到踢用户{}下线消息", UserTokenConstant.KICK_CHANNEL, user.getUuid()); + userResourceCheck.kickUser(user.getUuid()); } } diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/RedisConfig.java b/virtual-patient-web/src/main/java/com/supervision/websocket/RedisListener.java similarity index 93% rename from virtual-patient-web/src/main/java/com/supervision/usermanage/RedisConfig.java rename to virtual-patient-web/src/main/java/com/supervision/websocket/RedisListener.java index 68e38764..2cae1e58 100644 --- a/virtual-patient-web/src/main/java/com/supervision/usermanage/RedisConfig.java +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/RedisListener.java @@ -1,4 +1,4 @@ -package com.supervision.usermanage; +package com.supervision.websocket; import com.supervision.constant.UserTokenConstant; import org.springframework.beans.factory.annotation.Autowired; @@ -9,7 +9,7 @@ import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; @Configuration -public class RedisConfig { +public class RedisListener { @Autowired private KickUserListener kickUserListener; diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java b/virtual-patient-web/src/main/java/com/supervision/websocket/UserResourceCheck.java similarity index 52% rename from virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java rename to virtual-patient-web/src/main/java/com/supervision/websocket/UserResourceCheck.java index 8584aa62..b1056faa 100644 --- a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/UserResourceCheck.java @@ -1,12 +1,14 @@ -package com.supervision.usermanage; +package com.supervision.websocket; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.baomidou.lock.annotation.Lock4j; import com.supervision.constant.UserTokenConstant; -import com.supervision.controller.WebSocketServer; import com.supervision.exception.BusinessException; +import com.supervision.websocket.dto.KickDTO; +import com.supervision.websocket.dto.NoResourceDTO; +import com.supervision.websocket.dto.WebSocketLoginDTO; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,12 +17,9 @@ import org.springframework.beans.factory.annotation.Value; import javax.websocket.*; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.io.IOException; -import java.util.Map; -import java.util.Set; @Slf4j @Component @@ -41,17 +40,17 @@ public class UserResourceCheck { } @Lock4j(name = "achieveDiagnoseResourceAndOpenConnection") - public void achieveDiagnoseResourceAndOpenConnection(String uid, Session session) { + public void achieveDiagnoseResourceAndOpenConnection(String uuid, String userId, Session session) { // 如果小于数字人最大连接数,则可以连接 if (!achieveDiagnoseResource()) { + try { + session.getBasicRemote().sendText(JSONUtil.toJsonStr(new NoResourceDTO())); + } catch (Exception e) { + log.error("发送消息失败", e); + } throw new BusinessException("暂时没有资源,建立连接失败"); } - log.info("用户:{}开始登录,缓存到Redis,并尝试把该用户已有客户端踢下线", uid); - // 链接之前先把之前的用户踢下线(ignoreSessionId防止把当前用户踢下线) - // 注意,这里如果用户没有进到问诊页面,只是在问诊大厅时,是不会被踢掉的.(因为这时没有建立websocket连接,还没与放到SESSION_POOL里面去) - redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(uid, session.getId()))); - log.info("尝试踢该用户{}的其他session下线,忽略sessionId:{}", uid, session.getId()); - redisTemplate.opsForHash().put(UserTokenConstant.USER_WEBSOCKET_CACHE, uid, session.getId()); + redisTemplate.opsForHash().put(UserTokenConstant.USER_WEBSOCKET_CACHE, uuid, JSONUtil.toJsonStr(new WebSocketLoginDTO(uuid, userId, session.getId()))); } /** @@ -59,42 +58,27 @@ public class UserResourceCheck { */ // 实现一个方法用于踢下线用户,走的是Redis的消息队列 - public void kickUser(String userId, String ignoreSessionId) { - log.info("尝试踢用户:{}下线", userId); - Session session = WebSocketServer.SESSION_POOL.get(userId); + public void kickUser(String uuid) { + log.info("尝试踢uuid:{}下线", uuid); + Session session = WebSocketSessionPool.SESSION_POOL.get(uuid); // 只有不是忽略剔除的sessionId才可以踢下线 - if (ObjectUtil.isNotEmpty(session) && !StrUtil.equals(ignoreSessionId, session.getId())) { + if (ObjectUtil.isNotEmpty(session)) { try { - session.getBasicRemote().sendText(JSONUtil.toJsonStr(new Kick())); + session.getBasicRemote().sendText(JSONUtil.toJsonStr(new KickDTO())); session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "用户被踢下线")); - WebSocketServer.SESSION_POOL.remove(userId); - log.info("踢用户:{},sessionId:{} 下线成功", userId, session.getId()); + WebSocketSessionPool.SESSION_POOL.remove(uuid); + // 然后从redis中移除掉 + redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uuid); + log.info("踢UUID:{},sessionId:{} 下线成功", uuid, session.getId()); return; } catch (IOException e) { log.error("用户:{}的websocket连接异常", e.getMessage()); - // 连接异常的用户,移除 - WebSocketServer.SESSION_POOL.remove(userId); - // 移除redis中该用户的缓存 - redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, userId); + // TODO 如果用户连接异常,怎么办 + } } - log.info("踢用户:{}下线,未找到用户,踢下线失败", userId); - } - - @Data - public static class Kick { - private final Integer code = UserTokenConstant.KICK_CODE; - - private final String message = "用户被踢下线"; - + log.info("踢UUID:{}下线,未找到用户,踢下线失败", uuid); } - @Data - public static class Keepalive { - private final Integer code = UserTokenConstant.KEEPALIVE_CODE; - - private final String message = "keepalive"; - - } } diff --git a/virtual-patient-web/src/main/java/com/supervision/websocket/WebSocketSessionPool.java b/virtual-patient-web/src/main/java/com/supervision/websocket/WebSocketSessionPool.java new file mode 100644 index 00000000..e6d7070c --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/WebSocketSessionPool.java @@ -0,0 +1,13 @@ +package com.supervision.websocket; + +import com.supervision.websocket.dto.WebSocketLoginDTO; + +import javax.websocket.Session; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public class WebSocketSessionPool { + + //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。 + public static final ConcurrentHashMap SESSION_POOL = new ConcurrentHashMap<>(); +} diff --git a/virtual-patient-web/src/main/java/com/supervision/task/WebsocketKeepaliveTask.java b/virtual-patient-web/src/main/java/com/supervision/websocket/WebsocketKeepaliveTask.java similarity index 53% rename from virtual-patient-web/src/main/java/com/supervision/task/WebsocketKeepaliveTask.java rename to virtual-patient-web/src/main/java/com/supervision/websocket/WebsocketKeepaliveTask.java index c240ddc3..91f6a948 100644 --- a/virtual-patient-web/src/main/java/com/supervision/task/WebsocketKeepaliveTask.java +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/WebsocketKeepaliveTask.java @@ -1,13 +1,12 @@ -package com.supervision.task; +package com.supervision.websocket; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; -import com.supervision.constant.UserTokenConstant; -import com.supervision.controller.WebSocketServer; -import com.supervision.usermanage.UserResourceCheck; +import com.supervision.websocket.UserResourceCheck; +import com.supervision.websocket.WebSocketSessionPool; +import com.supervision.websocket.dto.KeepaliveDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -19,30 +18,20 @@ import java.util.Map; @Slf4j @RequiredArgsConstructor public class WebsocketKeepaliveTask { - - private final RedisTemplate redisTemplate; - @Scheduled(fixedDelay = 5 * 1000L) public void keepalive() { - log.info("websocket保活接口开始,每5秒钟发送一次消息"); - for (Map.Entry entries : WebSocketServer.SESSION_POOL.entrySet()) { + for (Map.Entry entries : WebSocketSessionPool.SESSION_POOL.entrySet()) { String userId = entries.getKey(); Session session = entries.getValue(); if (ObjectUtil.isNotEmpty(session)) { try { - - session.getBasicRemote().sendText(JSONUtil.toJsonStr(new UserResourceCheck.Keepalive())); + session.getBasicRemote().sendText(JSONUtil.toJsonStr(new KeepaliveDTO())); log.info("用户:{}的websocket保活成功", userId); } catch (IOException e) { log.error("用户:{}的websocket连接异常", userId, e); - // 连接异常的用户,移除 - WebSocketServer.SESSION_POOL.remove(userId); - // 移除redis中该用户的缓存 - redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, userId); } } } - log.info("websocket保活接口结束"); } } diff --git a/virtual-patient-web/src/main/java/com/supervision/websocket/dto/KeepaliveDTO.java b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/KeepaliveDTO.java new file mode 100644 index 00000000..2865c27f --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/KeepaliveDTO.java @@ -0,0 +1,12 @@ +package com.supervision.websocket.dto; + +import com.supervision.constant.UserTokenConstant; +import lombok.Data; + +@Data +public class KeepaliveDTO { + + private final Integer code = UserTokenConstant.KEEPALIVE_CODE; + + private final String message = "keepalive"; +} diff --git a/virtual-patient-web/src/main/java/com/supervision/websocket/dto/KickDTO.java b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/KickDTO.java new file mode 100644 index 00000000..6987fdcc --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/KickDTO.java @@ -0,0 +1,12 @@ +package com.supervision.websocket.dto; + +import com.supervision.constant.UserTokenConstant; +import lombok.Data; + +@Data +public class KickDTO { + + private final Integer code = UserTokenConstant.KICK_CODE; + + private final String message = "用户被踢下线"; +} diff --git a/virtual-patient-web/src/main/java/com/supervision/websocket/dto/NoResourceDTO.java b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/NoResourceDTO.java new file mode 100644 index 00000000..cbb32746 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/NoResourceDTO.java @@ -0,0 +1,10 @@ +package com.supervision.websocket.dto; + +import com.supervision.constant.UserTokenConstant; + +public class NoResourceDTO { + + private final Integer code = UserTokenConstant.NO_RESOURCE_CODE; + + private final String message = "用户被踢下线"; +} diff --git a/virtual-patient-web/src/main/java/com/supervision/websocket/dto/UserWebSocketDTO.java b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/UserWebSocketDTO.java new file mode 100644 index 00000000..f57d46f6 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/UserWebSocketDTO.java @@ -0,0 +1,12 @@ +package com.supervision.websocket.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class UserWebSocketDTO { + + private String uuid; + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/websocket/dto/WebSocketLoginDTO.java b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/WebSocketLoginDTO.java new file mode 100644 index 00000000..f4d4e60c --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/websocket/dto/WebSocketLoginDTO.java @@ -0,0 +1,28 @@ +package com.supervision.websocket.dto; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Data +public class WebSocketLoginDTO { + + private String userId; + + private String uuid; + + private String sessionId; + + private String loginTime; + + public WebSocketLoginDTO(String uuid, String userId, String sessionId) { + this.userId = userId; + this.uuid = uuid; + this.sessionId = sessionId; + this.loginTime = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + + public WebSocketLoginDTO() { + } +}