「这是我参与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;
}
}
好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊