|
@@ -48,16 +48,24 @@ import java.util.concurrent.ConcurrentHashMap;
|
|
|
@ServerEndpoint(value = "/ws/mobile", configurator = WebSocketMobileHandshakeInterceptor.class)
|
|
|
@Component
|
|
|
public class WebSocketMobileServer implements Concurrently {
|
|
|
+
|
|
|
private final static Logger log = LoggerFactory.getLogger(WebSocketMobileServer.class);
|
|
|
+
|
|
|
private volatile static ConcurrentHashMap<String, WebSocketMobileServer> webSocketMap = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
/**
|
|
|
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
|
|
|
*/
|
|
|
private Session session = null;
|
|
|
+
|
|
|
private String sessionId = null, ip = null, deviceId = null, websocketSessionId = null;
|
|
|
+
|
|
|
private Long recordId = null, updateTime = null;
|
|
|
+
|
|
|
private RedisUtil redisUtil;
|
|
|
+
|
|
|
private Map<String, Object> tranMap = null;
|
|
|
+
|
|
|
private MonitorVideoSourceEnum source = null;
|
|
|
|
|
|
/**
|
|
@@ -74,20 +82,30 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
session.setMaxIdleTimeout(SystemConstant.WEBSOCKET_MAX_TIME_OUT);
|
|
|
this.sessionId = tbSession.getId();
|
|
|
this.websocketSessionId = String.valueOf(UidUtil.nextId());
|
|
|
- if (webSocketMap.containsKey(this.websocketSessionId + "-" + this.source.name())) {
|
|
|
+ if (webSocketMap.containsKey(this.websocketSessionId)) {
|
|
|
throw new BusinessException(ExceptionResultEnum.REPEAT_CONNECT_ERROR);
|
|
|
} else {
|
|
|
- webSocketMap.put(this.websocketSessionId + "-" + this.source.name(), this);
|
|
|
+ webSocketMap.put(this.websocketSessionId, this);
|
|
|
addOnlineCount();
|
|
|
}
|
|
|
- log.info("用户连接:{},当前在线人数为:{}", this.websocketSessionId, getOnlineCount());
|
|
|
- InetSocketAddress addr = (InetSocketAddress) WebsocketUtil.getFieldInstance(this.session.getAsyncRemote(), "base#socketWrapper#socket#sc#remoteAddress");
|
|
|
+ log.info("考试记录:{},WS连接创建:{},在线人数:{}", this.recordId, this.websocketSessionId, getOnlineCount());
|
|
|
+ InetSocketAddress addr = (InetSocketAddress) WebsocketUtil
|
|
|
+ .getFieldInstance(this.session.getAsyncRemote(), "base#socketWrapper#socket#sc#remoteAddress");
|
|
|
this.ip = addr.toString().replace("/", "").split(":")[0];
|
|
|
log.info("ip[:{}]连接成功", this.ip);
|
|
|
- if (this.source == MonitorVideoSourceEnum.MOBILE_FIRST) {
|
|
|
- WebsocketUtil.updateExamRecordMobileFirstWebsocketStatus(this.recordId, this.websocketSessionId, WebsocketStatusEnum.ON_LINE);
|
|
|
- } else if (this.source == MonitorVideoSourceEnum.MOBILE_SECOND) {
|
|
|
- WebsocketUtil.updateExamRecordMobileSecondWebsocketStatus(this.recordId, this.websocketSessionId, WebsocketStatusEnum.ON_LINE);
|
|
|
+ //修改record相关状态需要强制上锁
|
|
|
+ String lockKey = SystemConstant.REDIS_LOCK_WEBSOCKET_PREFIX + ":" + this.source.name() + ":" + recordId;
|
|
|
+ redisUtil.waitLock(lockKey, SystemConstant.REDIS_LOCK_WEBSOCKET_TIME_OUT);
|
|
|
+ try {
|
|
|
+ if (this.source == MonitorVideoSourceEnum.MOBILE_FIRST) {
|
|
|
+ WebsocketUtil.updateExamRecordMobileFirstWebsocketStatus(this.recordId, this.websocketSessionId,
|
|
|
+ WebsocketStatusEnum.ON_LINE);
|
|
|
+ } else if (this.source == MonitorVideoSourceEnum.MOBILE_SECOND) {
|
|
|
+ WebsocketUtil.updateExamRecordMobileSecondWebsocketStatus(this.recordId, this.websocketSessionId,
|
|
|
+ WebsocketStatusEnum.ON_LINE);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ redisUtil.releaseLock(lockKey);
|
|
|
}
|
|
|
this.updateTime = System.currentTimeMillis();
|
|
|
tranMap = WebsocketUtil.initWebsocket(recordId, null, deviceId, ip, updateTime);
|
|
@@ -99,30 +117,42 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
@OnClose
|
|
|
public void onClose() {
|
|
|
log.info("onClose is come in");
|
|
|
- if (webSocketMap.containsKey(this.websocketSessionId + "-" + this.source.name())) {
|
|
|
- webSocketMap.remove(this.websocketSessionId + "-" + this.source.name());
|
|
|
+ if (webSocketMap.containsKey(this.websocketSessionId)) {
|
|
|
+ webSocketMap.remove(this.websocketSessionId);
|
|
|
//从set中删除
|
|
|
subOnlineCount();
|
|
|
- //判断是否是正常退出
|
|
|
- Long timestamp = System.currentTimeMillis();
|
|
|
- ExamRecordCacheUtil.setMonitorStatus(this.recordId, this.source, MonitorStatusSourceEnum.STOP, timestamp);
|
|
|
- if (this.source == MonitorVideoSourceEnum.MOBILE_FIRST) {
|
|
|
- ExamRecordCacheUtil.setMobileFirstWebsocketStatus(this.recordId, WebsocketStatusEnum.OFF_LINE, timestamp);
|
|
|
- } else if (this.source == MonitorVideoSourceEnum.MOBILE_SECOND) {
|
|
|
- ExamRecordCacheUtil.setMobileSecondWebsocketStatus(this.recordId, WebsocketStatusEnum.OFF_LINE, timestamp);
|
|
|
+ //修改record相关状态需要强制上锁
|
|
|
+ String lockKey = SystemConstant.REDIS_LOCK_WEBSOCKET_PREFIX + ":" + this.source.name() + ":" + recordId;
|
|
|
+ redisUtil.waitLock(lockKey, SystemConstant.REDIS_LOCK_WEBSOCKET_TIME_OUT);
|
|
|
+ try {
|
|
|
+ //判断是否是正常退出
|
|
|
+ long timestamp = System.currentTimeMillis();
|
|
|
+ //比较考试记录最新websocketId是否与当前websocketId相同
|
|
|
+ String currentId = ExamRecordCacheUtil.getMobileWebsocketId(recordId, this.source);
|
|
|
+ if (this.websocketSessionId != null && this.websocketSessionId.equals(currentId)) {
|
|
|
+ ExamRecordCacheUtil
|
|
|
+ .setMonitorStatus(this.recordId, this.source, MonitorStatusSourceEnum.STOP, timestamp);
|
|
|
+ ExamRecordCacheUtil
|
|
|
+ .setMobileWebsocketStatus(this.recordId, this.source, WebsocketStatusEnum.OFF_LINE,
|
|
|
+ timestamp);
|
|
|
+ TOeExamRecordService tOeExamRecordService = SpringContextHolder.getBean(TOeExamRecordService.class);
|
|
|
+ tOeExamRecordService.sendExamRecordDataSaveMq(this.recordId, timestamp);
|
|
|
+ MqUtil mqUtil = SpringContextHolder.getBean(MqUtil.class);
|
|
|
+ MqDtoService mqDtoService = SpringContextHolder.getBean(MqDtoService.class);
|
|
|
+ Map mqMap = new HashMap<>();
|
|
|
+ mqMap.put(SystemConstant.RECORD_ID, this.recordId);
|
|
|
+ mqMap.put("source", this.source.name());
|
|
|
+ //监控结束
|
|
|
+ MqDto mqDtoStop = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.MONITOR_STOP.name(),
|
|
|
+ recordId, MqTagEnum.MONITOR_STOP, String.valueOf(recordId), mqMap,
|
|
|
+ String.valueOf(recordId));
|
|
|
+ mqDtoService.assembleSendOneOrderMsg(mqDtoStop);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ redisUtil.releaseLock(lockKey);
|
|
|
}
|
|
|
- TOeExamRecordService tOeExamRecordService = SpringContextHolder.getBean(TOeExamRecordService.class);
|
|
|
- tOeExamRecordService.sendExamRecordDataSaveMq(this.recordId, timestamp);
|
|
|
- MqUtil mqUtil = SpringContextHolder.getBean(MqUtil.class);
|
|
|
- MqDtoService mqDtoService = SpringContextHolder.getBean(MqDtoService.class);
|
|
|
- Map mqMap = new HashMap<>();
|
|
|
- mqMap.put(SystemConstant.RECORD_ID, this.recordId);
|
|
|
- mqMap.put("source", this.source.name());
|
|
|
- //监控结束
|
|
|
- MqDto mqDtoStop = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.MONITOR_STOP.name(), recordId, MqTagEnum.MONITOR_STOP, String.valueOf(recordId), mqMap, String.valueOf(recordId));
|
|
|
- mqDtoService.assembleSendOneOrderMsg(mqDtoStop);
|
|
|
}
|
|
|
- log.info("用户退出:{},当前在线人数为:{},updateTime:{}", this.sessionId, getOnlineCount(), this.updateTime);
|
|
|
+ log.info("考试记录:{},WS连接关闭:{},在线人数:{}", this.recordId, this.websocketSessionId, getOnlineCount());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -141,7 +171,8 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
JSONObject jsonObject = JSONObject.parseObject(message);
|
|
|
log.info("onMessage:{}", jsonObject.toJSONString());
|
|
|
if (Objects.nonNull(jsonObject)) {
|
|
|
- WebSocketMobileMessageTemplete webSocketMobileMessageTemplete = SpringContextHolder.getBean(WebSocketMobileMessageTemplete.class);
|
|
|
+ WebSocketMobileMessageTemplete webSocketMobileMessageTemplete = SpringContextHolder
|
|
|
+ .getBean(WebSocketMobileMessageTemplete.class);
|
|
|
TOeExamRecordService tOeExamRecordService = SpringContextHolder.getBean(TOeExamRecordService.class);
|
|
|
MqUtil mqUtil = SpringContextHolder.getBean(MqUtil.class);
|
|
|
MqDtoService mqDtoService = SpringContextHolder.getBean(MqDtoService.class);
|
|
@@ -156,17 +187,23 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(this.recordId);
|
|
|
statusEnum = tOeExamRecord.getStatus();
|
|
|
}
|
|
|
- if (Objects.nonNull(statusEnum) && (statusEnum == ExamRecordStatusEnum.FINISHED ||
|
|
|
- statusEnum == ExamRecordStatusEnum.PERSISTED)) {
|
|
|
+ if (Objects.nonNull(statusEnum) && (statusEnum == ExamRecordStatusEnum.FINISHED
|
|
|
+ || statusEnum == ExamRecordStatusEnum.PERSISTED)) {
|
|
|
Map<String, Object> properties = new HashMap<>();
|
|
|
properties.put(SystemConstant.REMOVE_WEBSOCKET, true);
|
|
|
- MqDto mobileMqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_STOP.name(), recordId, MqTagEnum.EXAM_STOP, String.valueOf(recordId), properties, String.valueOf(recordId));
|
|
|
+ MqDto mobileMqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_STOP.name(),
|
|
|
+ recordId, MqTagEnum.EXAM_STOP, String.valueOf(recordId), properties,
|
|
|
+ String.valueOf(recordId));
|
|
|
mqDtoService.assembleSendOneOrderMsg(mobileMqDto);
|
|
|
}
|
|
|
- WebsocketTypeEnum websocketTypeEnum = WebsocketTypeEnum.valueOf(websocketDto.getType().toUpperCase());
|
|
|
+ WebsocketTypeEnum websocketTypeEnum = WebsocketTypeEnum
|
|
|
+ .valueOf(websocketDto.getType().toUpperCase());
|
|
|
if (Objects.nonNull(websocketTypeEnum) && websocketTypeEnum == WebsocketTypeEnum.SYNC_STATUS) {
|
|
|
- Method method = webSocketMobileMessageTemplete.getClass().getDeclaredMethod(websocketTypeEnum.getDesc(), String.class, Long.class);
|
|
|
- WebsocketDto result = (WebsocketDto) method.invoke(webSocketMobileMessageTemplete, String.valueOf(websocketDto.getBody()), websocketDto.getTime());
|
|
|
+ Method method = webSocketMobileMessageTemplete.getClass()
|
|
|
+ .getDeclaredMethod(websocketTypeEnum.getDesc(), String.class, Long.class);
|
|
|
+ WebsocketDto result = (WebsocketDto) method
|
|
|
+ .invoke(webSocketMobileMessageTemplete, String.valueOf(websocketDto.getBody()),
|
|
|
+ websocketDto.getTime());
|
|
|
this.sendMessage(result);
|
|
|
}
|
|
|
}
|
|
@@ -184,7 +221,7 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
*/
|
|
|
@OnError
|
|
|
public void onError(Session session, Throwable error) throws IOException {
|
|
|
- log.error("用户错误:{},原因:{}", this.websocketSessionId, error.getMessage());
|
|
|
+ log.error("考试记录:{},WS连接错误:{},原因:{}", this.recordId, this.websocketSessionId, error.getMessage());
|
|
|
close(this);
|
|
|
throw new BusinessException(error.getMessage());
|
|
|
}
|
|
@@ -215,42 +252,14 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
* 在线人数加一
|
|
|
*/
|
|
|
public synchronized void addOnlineCount() {
|
|
|
- if (redisUtil.lock(SystemConstant.REDIS_LOCK_WEBSOCKET_PREFIX + this.websocketSessionId, SystemConstant.REDIS_LOCK_WEBSOCKET_TIME_OUT)) {
|
|
|
- try {
|
|
|
- Object o = redisUtil.get(SystemConstant.WEBSOCKET_MOBILE_ONLINE_COUNT);
|
|
|
- int count = 0;
|
|
|
- if (Objects.nonNull(o)) {
|
|
|
- count = (int) o;
|
|
|
- }
|
|
|
- count++;
|
|
|
- redisUtil.set(SystemConstant.WEBSOCKET_MOBILE_ONLINE_COUNT, count);
|
|
|
- } finally {
|
|
|
- if (Objects.nonNull(this.websocketSessionId)) {
|
|
|
- redisUtil.releaseLock(SystemConstant.REDIS_LOCK_WEBSOCKET_PREFIX + this.websocketSessionId);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ redisUtil.increment(SystemConstant.WEBSOCKET_MOBILE_ONLINE_COUNT);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 在线人数减一
|
|
|
*/
|
|
|
public synchronized void subOnlineCount() {
|
|
|
- if (redisUtil.lock(SystemConstant.REDIS_LOCK_WEBSOCKET_PREFIX + this.websocketSessionId, SystemConstant.REDIS_LOCK_WEBSOCKET_TIME_OUT)) {
|
|
|
- try {
|
|
|
- Object o = redisUtil.get(SystemConstant.WEBSOCKET_MOBILE_ONLINE_COUNT);
|
|
|
- int count = 0;
|
|
|
- if (Objects.nonNull(o)) {
|
|
|
- count = (int) o;
|
|
|
- }
|
|
|
- count--;
|
|
|
- redisUtil.set(SystemConstant.WEBSOCKET_MOBILE_ONLINE_COUNT, count < 0 ? 0 : count);
|
|
|
- } finally {
|
|
|
- if (Objects.nonNull(this.websocketSessionId)) {
|
|
|
- redisUtil.releaseLock(SystemConstant.REDIS_LOCK_WEBSOCKET_PREFIX + this.websocketSessionId);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ redisUtil.decrement(SystemConstant.WEBSOCKET_MOBILE_ONLINE_COUNT);
|
|
|
}
|
|
|
|
|
|
public static ConcurrentHashMap<String, WebSocketMobileServer> getWebSocketMap() {
|
|
@@ -264,8 +273,10 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
*/
|
|
|
public static void close(WebSocketMobileServer webSocketMobileServer) throws IOException {
|
|
|
//判断当前连接是否还在线
|
|
|
- if (Objects.nonNull(webSocketMobileServer) && Objects.nonNull(webSocketMobileServer.session) && webSocketMobileServer.session.isOpen()) {
|
|
|
- webSocketMobileServer.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, SystemConstant.WEBSOCKET_CLOSE));
|
|
|
+ if (Objects.nonNull(webSocketMobileServer) && Objects.nonNull(webSocketMobileServer.session)
|
|
|
+ && webSocketMobileServer.session.isOpen()) {
|
|
|
+ webSocketMobileServer.session
|
|
|
+ .close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, SystemConstant.WEBSOCKET_CLOSE));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -274,7 +285,8 @@ public class WebSocketMobileServer implements Concurrently {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
|
|
|
+ public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
|
|
|
+ ConsumeConcurrentlyContext consumeConcurrentlyContext) {
|
|
|
MqOeLogicService mqOeLogicService = SpringContextHolder.getBean(MqOeLogicService.class);
|
|
|
try {
|
|
|
long threadId = Thread.currentThread().getId();
|