|
@@ -0,0 +1,183 @@
|
|
|
+package com.ruoyi.equity.socket;
|
|
|
+
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.ruoyi.common.utils.SecurityUtils;
|
|
|
+import com.ruoyi.equity.domain.vo.MsgVO;
|
|
|
+import com.ruoyi.equity.domain.vo.UserInfoVO;
|
|
|
+import com.ruoyi.equity.event.WebsocketEvent;
|
|
|
+import com.ruoyi.equity.facade.EquityMessageRecordFacade;
|
|
|
+import com.ruoyi.equity.service.IEquityMessageRecordService;
|
|
|
+import lombok.Data;
|
|
|
+import lombok.NonNull;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.BeansException;
|
|
|
+import org.springframework.context.ApplicationContext;
|
|
|
+import org.springframework.context.ApplicationContextAware;
|
|
|
+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.List;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
+import java.util.concurrent.locks.ReentrantLock;
|
|
|
+
|
|
|
+@Data
|
|
|
+@Component
|
|
|
+@ServerEndpoint(value = "/websocket/{userId}")
|
|
|
+public class WebSocketService implements ApplicationContextAware {
|
|
|
+
|
|
|
+ private static ApplicationContext applicationContext;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
|
|
+ WebSocketService.applicationContext = applicationContext;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**日志*/
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(WebSocketService.class);
|
|
|
+
|
|
|
+ /**静态变量,用来记录当前在线用户数。应该把它设计成线程安全的。*/
|
|
|
+ private static int onlineUserCount = 0;
|
|
|
+
|
|
|
+ /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
|
|
|
+ private static int onlineLinkCount = 0;
|
|
|
+
|
|
|
+ /**用户锁的映射表*/
|
|
|
+ private static final ConcurrentHashMap<Long, ReentrantLock> locks = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ /**concurrent包的线程安全Set,用来存放每个用户对应的session对象*/
|
|
|
+ public static final ConcurrentHashMap<Long, CopyOnWriteArrayList<Session>> userSessionMap = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
|
|
|
+ private Session session;
|
|
|
+ /**用户id*/
|
|
|
+ private Long userId;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接建立成功调用的方法
|
|
|
+ */
|
|
|
+ @OnOpen
|
|
|
+ public void onOpen(Session session, @PathParam("userId") Long userId) {
|
|
|
+ ReentrantLock lock = locks.computeIfAbsent(userId, k -> new ReentrantLock());
|
|
|
+ lock.lock();
|
|
|
+ CopyOnWriteArrayList<Session> sessions = userSessionMap.computeIfAbsent(userId, k -> new CopyOnWriteArrayList<>());
|
|
|
+ if (sessions.isEmpty()){
|
|
|
+ addOnlineUserCount();
|
|
|
+ }
|
|
|
+ addOnlineLinkCount();
|
|
|
+ sessions.add(session);
|
|
|
+ this.session = session;
|
|
|
+ this.userId= userId;
|
|
|
+ log.info("----------------------建立连接-------------------------------");
|
|
|
+ log.info(StrUtil.format("用户{}连接成功,当前用户总连接数:{}", userId, sessions.size()));
|
|
|
+ log.info(StrUtil.format("当前在线人数为:{},当前总连接数:{}", getOnlineUserCount(),getOnlineLinkCount()));
|
|
|
+ UserInfoVO vo = UserInfoVO.builder().userId(userId).build();
|
|
|
+ try {
|
|
|
+ WebsocketEvent event = new WebsocketEvent("websocketService");
|
|
|
+ event.setUserInfoVO(vo);
|
|
|
+ applicationContext.publishEvent(event);
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("用户:"+userId+",网络异常:"+e.getMessage());
|
|
|
+ }finally {
|
|
|
+ lock.unlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接关闭调用的方法
|
|
|
+ */
|
|
|
+ @OnClose
|
|
|
+ public void onClose() {
|
|
|
+ log.info("----------------------关闭连接-------------------------------");
|
|
|
+ ReentrantLock lock = locks.computeIfAbsent(userId, k -> new ReentrantLock());
|
|
|
+ lock.lock();
|
|
|
+ try {
|
|
|
+ CopyOnWriteArrayList<Session> sessions = userSessionMap.get(userId);
|
|
|
+ if (CollUtil.isEmpty(sessions)) {
|
|
|
+ throw new RuntimeException("sessions中未找到当前连接");
|
|
|
+ }
|
|
|
+ sessions.remove(session);
|
|
|
+ subOnlineLinkCount();
|
|
|
+ if (CollUtil.isEmpty(sessions)){
|
|
|
+ userSessionMap.remove(userId);
|
|
|
+ subOnlineUserCount();
|
|
|
+ log.info(StrUtil.format("用户{}退出,当前在线人数为:{}", userId, getOnlineUserCount()));
|
|
|
+ }else {
|
|
|
+ log.info(StrUtil.format("用户{}断开连接,用户当前剩余连接:{},当前在线人数为:{}", userId, sessions.size(),getOnlineUserCount()));
|
|
|
+ }
|
|
|
+ }catch (Exception e){
|
|
|
+ log.info("用户"+userId + "断开连接时发生异常:"+e.getMessage());
|
|
|
+ }finally {
|
|
|
+ lock.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收到客户端消息后调用的方法
|
|
|
+ * @param message 客户端发送过来的消息*/
|
|
|
+ @OnMessage
|
|
|
+ public void onMessage(String message, Session session) {
|
|
|
+ log.info("收到用户消息:"+userId+",报文:"+message);
|
|
|
+ }
|
|
|
+
|
|
|
+ @OnError
|
|
|
+ public void onError(Session session, Throwable error) {
|
|
|
+ log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
|
|
|
+ error.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void sendMessage(UserInfoVO vo){
|
|
|
+ List<Session> sessions = userSessionMap.get(vo.getUserId());
|
|
|
+ if(sessions != null && sessions.size() > 0){
|
|
|
+ /** 此处不能使用普通集合,否则在推送消息时,当前用户又开启一个页面,集合发生变化迭代器会报错ConcurrentModificationException*/
|
|
|
+ for (Session s : sessions) {
|
|
|
+ if (s == null){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ RemoteEndpoint.Basic basicRemote = s.getBasicRemote();
|
|
|
+ /** 推送消息时可能因为nginx配置的连接时间,或者用户刚好断开连接,
|
|
|
+ * 导致通道关闭,但是数组中使用的是快照,还存在这个连接,此时跳过推送给这个窗口*/
|
|
|
+ if (basicRemote != null){
|
|
|
+ try {
|
|
|
+ basicRemote.sendText(vo.getMsg());
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(vo.getUserId()+"推送消息失败,消息内容["+ vo.getMsg()+"],原因:"+e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized void addOnlineUserCount() {
|
|
|
+ WebSocketService.onlineUserCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized void subOnlineUserCount() {
|
|
|
+ WebSocketService.onlineUserCount--;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static synchronized void addOnlineLinkCount() {
|
|
|
+ WebSocketService.onlineLinkCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized void subOnlineLinkCount() {
|
|
|
+ WebSocketService.onlineLinkCount--;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized int getOnlineUserCount() {
|
|
|
+ return onlineUserCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized int getOnlineLinkCount() {
|
|
|
+ return onlineLinkCount;
|
|
|
+ }
|
|
|
+}
|