Spring Websocket Session集群

news/2024/11/8 17:07:00/

WebSocketSession无法序列化到redis,因此在集群中,我们无法将所有WebSocketSession都缓存到redis进行session共享。但是,我们可以使用redis的发布订阅模式解决。

1、WebSocketHandler 消息处理器

import com.alibaba.fastjson.JSON;
import com.techhf.common.core.constant.SecurityConstants;
import com.techhf.common.core.exception.BusinessServiceException;
import com.techhf.im.api.model.vo.ChatPageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** websocket 消息处理器** @author ronshi* @date 2022/11/18 9:17*/
@Slf4j
@Component
public class WebSocketHandler extends TextWebSocketHandler {/*** 冒号分隔符*/public static final String SEMICOLON = ":";/*** redis 订阅通道名*/public static final String CHANNEL_NAME = "imRedisTopic";/*** 当前节点在线session*/public static final Map<String, WebSocketSession> POOL_SESSION = new ConcurrentHashMap<>();@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {String key = getKey(session);POOL_SESSION.put(key, session);}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {String key = getKey(session);POOL_SESSION.remove(key);}private String getKey(WebSocketSession session) {String userType = (String) session.getAttributes().get(SecurityConstants.DETAILS_USERTYPE);String userId = (String) session.getAttributes().get(SecurityConstants.DETAILS_USER_ID);String key = userType + SEMICOLON + userId;if (userId == null) {throw new BusinessServiceException("未认证");}return key;}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 获得客户端传来的消息String payload = message.getPayload();//log.info("server 接收到消息 {}", payload);session.sendMessage(new TextMessage(payload));}/*** 发送消息*/public boolean sendMsg(ChatPageVO message) {String key = message.getAnotherUserType() + SEMICOLON + message.getAnotherUserId();if (POOL_SESSION.containsKey(key)) {try {POOL_SESSION.get(key).sendMessage(new TextMessage(JSON.toJSONString(message)));} catch (IOException e) {log.error("发送消息给{}失败", key, e);return false;}} else {// 发布消息redisTemplate.convertAndSend(CHANNEL_NAME, JSON.toJSONString(message));}return true;}}

2、RedisMessageListener 订阅发布监听类

import com.alibaba.fastjson.JSON;
import com.techhf.im.api.model.vo.ChatPageVO;
import com.techhf.im.biz.ws.WebSocketHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;/*** Redis订阅发布监听类** @author ronshi* @date 2023/6/7 16:00*/
@Slf4j
@Component
public class RedisMessageListener implements MessageListener {@Resourceprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic void onMessage(Message message, byte[] bytes) {// 获取消息Object messageBody = redisTemplate.getValueSerializer().deserialize(message.getBody());if (messageBody == null) {return;}String content = messageBody.toString();ChatPageVO chatMessage = JSON.parseObject(content, ChatPageVO.class);String key = chatMessage.getAnotherUserType() + WebSocketHandler.SEMICOLON + chatMessage.getAnotherUserId();//当前节点在线sessionMap<String, WebSocketSession> onlineSessionMap = WebSocketHandler.POOL_SESSION;if (onlineSessionMap.containsKey(key)) {try {onlineSessionMap.get(key).sendMessage(new TextMessage(content));} catch (IOException e) {log.error("发送消息给{}失败", key, e);}}}
}

3、RedisSubscriberConfig 订阅发布模式的容器配置

import com.techhf.im.biz.ws.WebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;/*** 订阅发布模式的容器配置** @author ronshi* @date 2023/6/7 17:01*/
@Configuration
@AutoConfigureAfter({RedisMessageListener.class})
public class RedisSubscriberConfig {@Autowiredprivate RedisMessageListener listener;/*** 创建消息监听容器** @param redisConnectionFactory redis连接工厂* @return 监听容器*/@Beanpublic RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(redisConnectionFactory);container.addMessageListener(listener, new ChannelTopic(WebSocketHandler.CHANNEL_NAME));return container;}
}


http://www.ppmy.cn/news/276041.html

相关文章

唯美girls

import requests ,re from fake_useragent import UserAgent import time,os#请求网页 url"https://www.vmgirls.com/13344.html" headers {"User-Agent":UserAgent().chrome } #表明自己的一个身份 rrequests.get(url,headersheaders) #print(r.request.h…

童年游戏 世嘉MD 美少女战士 最高难度 一命通关视频

美少女战士是MD平台的一款横版格斗游戏&#xff0c;该款游戏改编自当是的热门同名漫画《美少女战士》。动漫里的各种美女帅哥都有登场&#xff0c;每当主角打BOSS的时候&#xff0c;男主角就会出现并扔下一枝玫瑰花&#xff0c;捡了花之后生命值就会回满。 观看地址 https://w…

少女漫「不思议游戏」新作

【M站 老南街 整理报道】昨日渡濑悠宇的经典少女漫画「不思议游戏」才宣布将要舞台剧化&#xff0c;以喜矢武丰将饰演鬼宿为故事主角来展开&#xff01;在今日发售的flowers3月号又公布了最新消息&#xff01;渡濑玩完玄武篇之后&#xff0c;如今决定要出白虎篇了。将会在2月28…

蔷薇

蔷薇&#xff08;学名&#xff1a;Rosa sp.&#xff09; 别称&#xff1a;多花蔷薇、蔓性蔷薇、墙蘼、刺蘼、蔷蘼、刺莓苔、野蔷薇 蔷薇属部分植物的通称&#xff0c;主要指蔓藤蔷薇的变种及园艺品种 产地生境&#xff1a;喜生于路旁、田边或丘陵地的灌木丛中&#xff0c;产中国…

魔法少女小Scarlet

** 魔法少女小Scarlet ** 题目 魔法少女小Scarlet 题目描述输入格式输出格式输入输出样例思路代码 题目描述 Scarlet 最近学会了一个数组魔法&#xff0c;她会在 nn 二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 90 。 首先&#xff0c;Scarlet 会把 1 到 n^2的正整…

国内原创精品《萝莉的远征》(ADV+RPG )

原帖&#xff1a;http://3dmgame.chnren.com/bbs/showtopic-604395.html 这个是标题&#xff0c;因为去年开始做的时候就采用了这张图做标题&#xff0c;所以最终版也没有更换&#xff0c;只是修饰了一下呵呵。游戏名称&#xff1a;萝莉的远征RPG1ST银色幻境版本信息&#xff…

蔷薇变成玫瑰

你总希望我为你改变&#xff0c;从蔷薇变成玫瑰&#xff0c;可问题是&#xff0c;爱着你的是蔷薇&#xff0c;待我变成了玫瑰&#xff0c;还会爱着你吗?

[转]青涩的少女心结

秋张曾经是哈利迷俱乐部里人气最高的角色之一。从三年级时初见秋张时&#xff0c;哈利就对她有莫名的好感。上场比赛前&#xff0c;作为对方找球手的秋只是对哈利微微一笑&#xff0c;哈利的心就一阵狂跳。这种好感在四年级的时候升级&#xff0c;哈利总是默默关注秋张&#xf…