SpringBoot结合WebSocket开发实时消息推送(三)

384 阅读4分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

1、前言

​ 在上一篇的分享中,我为大家介绍了WebSocket中前端代码的书写,本次就为大家介绍下后端代码的具体应用。

2、后端代码

​ 现在pom文件中引入WebSocket的jar包,这里我是放在了一个公依赖包里,然后又对WebSocket作恶一个简单封装。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
//WebSocket容器
public class WebSocketContainer { 
	//serviceCode-->sessions
	private static Map<String,List<CwebSocket>> socketContainer =new ConcurrentHashMap<String, List<CwebSocket>>();
	
	private static Map<String, ConcurrentHashMap<String, WebSocketMsg>> webSocketContainer = new ConcurrentHashMap<String, ConcurrentHashMap<String, WebSocketMsg>>();
	
	/**
	 * 添加一个session连接信息
	 * @param serviceCode   服务标识
	 * @param userName      用户名
	 * @param session       连接信息
	 */
	public static void addScoket(String serviceCode,String userName, Session session){
		List<CwebSocket> CwebSockets=socketContainer.get(serviceCode);
		if (CwebSockets == null){
			CwebSockets = new ArrayList<CwebSocket>();
			socketContainer.put(serviceCode,CwebSockets);
		}
		CwebSockets.add(new CwebSocket(userName,session));
		logger.info("添加session成功,sessionId:{},serviceCode:{},userName:{}",session.getId(),serviceCode,userName);
	}

	public static void delSocket(String serviceCode,Session session){
		List<CwebSocket> CwebSockets=socketContainer.get(serviceCode);
		if (CwebSockets!=null && session!=null){
			Iterator<CwebSocket> iterator=CwebSockets.iterator();
			while (iterator.hasNext()){
				CwebSocket next=iterator.next();
				if (session == next.getSession()){
					iterator.remove();
					break;
				}
			}
		}
	}

	public static void sendMsg(String serviceCode,String userName,String msg) throws IOException {
		if (!StringUtils.isEmpty(serviceCode)){
			List<CwebSocket> CwebSockets=socketContainer.get(serviceCode);
			if (CwebSockets!=null){
				for (CwebSocket CwebSocket:CwebSockets){
					if (StringUtils.isNotEmpty(userName)){
						if (CwebSocket.getUserName().equalsIgnoreCase(userName)){
							CwebSocket.getSession().getBasicRemote().sendText(msg);
						}
					}else{
						CwebSocket.getSession().getBasicRemote().sendText(msg);
					}
				}
			}
		}
	}
	
	//保存当前websocket的session信息 
	public static void setWebsocketSession(String serviceCode, String key, WebSocketMsg webSocketMsg){
		ConcurrentHashMap<String, WebSocketMsg> serviceMap = null;
		if(webSocketContainer.containsKey(serviceCode)) {
			serviceMap = webSocketContainer.get(serviceCode);
		}else {
			serviceMap = new ConcurrentHashMap<String, WebSocketMsg>();
			webSocketContainer.put(serviceCode, serviceMap);
		}
		serviceMap.put(key, webSocketMsg);
	}
	
	/**
	 * @Title:       getWebsocketSession
	 * @Description: 获取当前websocket的session信息  
	 * 备注:多节点部署时,可能会出现PromptException异常,需要代码处理异常,不可直接抛出
	 */
	public static WebSocketMsg getWebsocketSession(String serviceCode, String key) throws PromptException {
		if(webSocketContainer.containsKey(serviceCode)) {
			ConcurrentHashMap<String, WebSocketMsg> serviceMap = webSocketContainer.get(serviceCode);
			if(serviceMap.containsKey(key)) {
				return serviceMap.get(key);
			}else {
				throw new PromptException("当前容器不包含serviceCode[" + serviceCode + "]");
			}
		}else {
			throw new KPromptException("当前容器serviceCode[" + serviceCode + "],不包含WebSocketMsg[" + key + "]");
		}
	}
	
	/**
	 * @Title:       setWebsocketSession
	 * @Description: 删除容器websocket的session信息 
	 */
	public static void removeWebsocketSession(String serviceCode, String key) {
		if(webSocketContainer.containsKey(serviceCode)) {
			ConcurrentHashMap<String, WebSocketMsg> serviceMap = webSocketContainer.get(serviceCode);
			if(serviceMap.containsKey(key)) {
				serviceMap.remove(key);
			}
		}
	}

	/**
	 * @Title:       getWebSocketContainer
	 * @Description: 获取容器 
	 */
	public static Map<String, WebSocketMsg> getWebSocketContainer(String serviceCode) {
		return webSocketContainer.get(serviceCode);
	}
	
} 

//处理WebSocket消息
@ServerEndpoint("/Cwebsocket")
@Component
public class WebSocketAction { 
	
	@OnOpen
	public void open(Session session){
		Map<String, List<String>> params = session.getRequestParameterMap();
		String serviceCode=params.get("serviceCode").get(0);
		if(params.containsKey("mqQuotaName") || params.containsKey("mqExchangeName")){
			String sessionId = session.getId();
			WebSocketMsg webSocketMsg = new WebSocketMsg("", sessionId, session);
			WebSocketContainer.setWebsocketSession(serviceCode, sessionId, webSocketMsg);
		}else {
			String sys_user_loginname = StringUtils.EMPTY;
			if(params.containsKey("sys_user_loginname")) {
				sys_user_loginname = params.get("sys_user_loginname").get(0);
			}
			WebSocketContainer.addScoket(serviceCode,sys_user_loginname,session);
		}
	}

	@OnClose
	public void close(Session session){
		Map<String, List<String>> params = session.getRequestParameterMap();
		String serviceCode=params.get("serviceCode").get(0);
		if(params.containsKey("mqQuotaName") || params.containsKey("mqExchangeName")){
			WebSocketContainer.removeWebsocketSession(serviceCode, session.getId());
		}else {
			WebSocketContainer.delSocket(serviceCode,session);
		}
	}

	@OnMessage
	public void message(String msg,Session session){
		log.info("webSocket收到前端消息:" + msg);
		Map<String, List<String>> params = session.getRequestParameterMap();
		String serviceCode=params.get("serviceCode").get(0);
		//先将session放入容器
		String sessionId = session.getId();
		WebSocketMsg webSocketMsg = new WebSocketMsg(msg, sessionId, session);
		WebSocketContainer.setWebsocketSession(serviceCode, sessionId, webSocketMsg);
		//将消息发送到对应的处理器
		RabbitTemplate rabbitTemplate = SysBeans.getBean("rabbitTemplate");
		if(params.containsKey("mqQuotaName")) {
			rabbitTemplate.convertAndSend(params.get("mqQuotaName").get(0), sessionId);
		}else if(params.containsKey("mqExchangeName") && !params.containsKey("mqExchangeName")) {
			rabbitTemplate.convertAndSend(params.get("mqExchangeName").get(0), sessionId);
		}else if(params.containsKey("mqRoutingKeyName") && params.containsKey("mqExchangeName")) {
			rabbitTemplate.convertAndSend(params.get("mqExchangeName").get(0), params.get("mqRoutingKeyName").get(0), sessionId);
		}else {
			log.error("webSocket收到前端消息处理器配置设置错误,配置信息为:mqQuotaName[" + params.toString() + "]");
		}
	}

	@OnError  //前端直接关闭页面,不调用close方法可以导致该错误
	public void onError(Session session, Throwable err) throws IOException {
		log.warn("websocket异常信息为:" + err.getMessage(), err, "session连接断开,sessionId:[" + session.getId() + "]");
		close(session);
		session.close();
	}

}

​ 将上述工具类封装好,放入公共依赖包后,这样就可以把WebSocket中前端传到后端的消息通过MQ转发到具体的服务中。然后,将后端向前端发送消息的功能单独放到一个服务内。

//WebSocket消息实时推送
@Component
public class FXPositionRTM { 
	
	public static final String serviceCode = "fxPositionRTM";
	
	@Autowired
	private FXPositionRTMUtil fXPositionRTMUtil;
	
	/** 
	 * @Description: 实时监控数据发送
	 * @param:       实时监控发送数据 
	 */
	@RabbitListener(queues = "fxPositionRTMQueue")
	@RabbitHandler
	public void fxPositionRTM(WebSocketMsg receivedSocketMsg) {
		try {
			String sessionId = receivedSocketMsg.getSessionId();
			//收到的、未处理的需要发送到前端的报文 
        	String receMsg = receivedSocketMsg.getMsg(); 
			if (StringUtils.isNotBlank(receMsg)) {
				if(StringUtils.isNotBlank(sessionId)) {
	            	WebSocketMsg webSocketMsg;
					try {
						//查询条件
						webSocketMsg = WebSocketContainer.getWebsocketSession(serviceCode, sessionId);
						String queryMsg = webSocketMsg.getMsg();
		            	//根据查询条件处理发送报文
		            	String sendMsg = dealReSocketMsg(queryMsg, receMsg);
		            	webSocketMsg.getSession().getBasicRemote().sendText(sendMsg); 
					} catch (PromptException e) {
						logger.error("获取websocket链接信息异常,异常信息为:{}, 异常对象为:{}, websocket对象信息为:{}!" ,e.getMessage(), e, receivedSocketMsg);  
					}
	            }else {
	            	//轮询所有session 筛选数据 推送消息
	            	Map<String, WebSocketMsg> webSocketContainer = WebSocketContainer.getWebSocketContainer(serviceCode);
	            	for (Entry<String, WebSocketMsg> entry : webSocketContainer.entrySet()){
	            		String qg = entry.getValue().getMsg();// 查询条件消息
	            		String dealReSocketMsg = dealReSocketMsg(qg, receMsg);
	            		entry.getValue().getSession().getBasicRemote().sendText(dealReSocketMsg); 
	                }
	            }
			}else {
				logger.info("实时监控数据发送,收到队列消息为空!websocket对象信息为:{}", receivedSocketMsg.getMsg());
			}
		} catch (Exception e) {
			logger.error("实时报价监控,异常信息为:{},websocket对象信息为:{}", e.getMessage(), receivedSocketMsg.getMsg());
		}
	}
	
	/** 
	 * @Description: 根据查询条件筛选实时交易额信息 
	 */ 
	private String dealReSocketMsg(String queryMasg, String receMsg) {
		Map<String, Object> queryParams = new HashMap<String, Object>();
		queryParams = JSON.parseObject(queryMasg, queryParams.getClass());
		//所有的交易额 
		Map<String, Object> receFPOSData = new HashMap<String, Object>();
		receFPOSData = JSON.parseObject(receMsg, receFPOSData.getClass());
		if (null == receFPOSData) {
			return "";
		}
		logger.info("交易额实时推送websocket端--查询条件为:{}", queryParams);
		String company = getMapVal(queryParams, "company");
		String desk = getMapVal(queryParams, "desk");
		String book = getMapVal(queryParams, "book");
		String ccyPair = getMapVal(queryParams, "ccy_pair");
		if (queryParams.containsKey("flag") && StringUtils.isNotBlank(queryParams.get("flag").toString())) {
			//具体某一个货物的交易额
			Map<String, Object> allTDPXMap = (Map<String, Object>) receFPOSData.get("cdbc");
			Iterator<Entry<String, Object>> iterator = allTDPXMap.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<String, Object> it = iterator.next();
				String key = it.getKey();
				if (!key.contains("company:" + company + "ccypair:" + ccyPair)) {
					iterator.remove();
					continue;
				} 
				if (StringUtils.isNotBlank(book) && !key.contains("book:" + book)) {
					//查询条件存在账户
					iterator.remove();
					continue;
				}
			}
			logger.info("交易额实时推送websocket端--根据查询条件筛选完后的交易额元数据为:{}", allTDPXMap);
			String resString = JSON.toJSONString(fXPositionRTMUtil.dealTBTSAFXPOSData(allTDPXMap));
			logger.info("交易额实时推送websocket端--将筛选后的元数据分类规整为:{}", resString);
			return resString;
		}else {
			String ccy = queryParams.get("ccy").toString();
			//Totals
			Map<String, Object> allTotals = (Map<String, Object>) receFPOSData.get("totals");
			Iterator<Entry<String, Object>> iterator = allTotals.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<String, Object> it = iterator.next();
				String key = it.getKey();
				if (StringUtils.isNotBlank(company) && !key.contains("company:" + company)) {
					//查询条件存在法人 且 查出来的交易额信息不包含此法人 那么需要将此条移除
					iterator.remove();
					continue;
				}
				if (StringUtils.isNotBlank(ccyPair) && !key.contains("ccypair:" + ccyPair)) {
					//查询条件存在货币对
					iterator.remove();
					continue;
				}
				if (StringUtils.isNotBlank(ccy) && !key.matches(".*" + ccy + ".*")) {
					//查询条件存在币种
					iterator.remove();
					continue;
				} 
				if (StringUtils.isNotBlank(book) && !key.contains("book:" + book)) {
					//查询条件存在账户
					iterator.remove();
					continue;
				}
			} 
			String resString = JSON.toJSONString(fXPositionRTMUtil.dealTotalsFXPOSData(allTotals)); 
			return resString;
		}
	}
	
	//根据Key获取Map中的Val Key不存在时返回""
	private String getMapVal(Map<String, Object> tarMap, String key) {
		if (tarMap.containsKey(key)) {
			return tarMap.get(key).toString();
		}
		return StringUtils.EMPTY;
	}
	
}

好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊