当前位置: 首页 > news >正文

持久化SSE对象

SpringBoot整合SSE,实现后端主动推送DEMO

前些日子写了整合SSE得demo。但是SSE对象是存储在ConcurrentHashMap<String, SseEmitter>中。在正式环境明显就不行了,服务重启一下的话都没有了。

那么要持久化,第一选择放redis

1、写了一个redis操作组件

SseEmitterStore

/*** 不考虑redis 连接异常问题* @author cmy* @date 2024/8/21 10:55*/
@Component
public class SseEmitterStore {private ConcurrentHashMap<String, SseEmitter> emitters = new ConcurrentHashMap<>();@Resourceprivate RedisTemplate<String, Object> redisTemplate;public void addEmitter(String key, SseEmitter emitter) {emitters.put(key, emitter);redisTemplate.opsForHash().put("sse-emitters", key, emitter);}public void removeEmitter(String key) {emitters.remove(key);redisTemplate.opsForHash().delete("sse-emitters", key);}@PostConstructprivate void init() {Map<Object, Object> temp = redisTemplate.opsForHash().entries("sse-emitters");temp.forEach((key, value) -> {if (value instanceof SseEmitter) {emitters.put(key.toString(), (SseEmitter) value);}});}public ConcurrentHashMap<String, SseEmitter> getEmitters() {return emitters;}
}

Controller修改

public class SseController {@ResourceSseEmitterStore sseEmitterStore;@GetMapping("/subscribe/{id}")@CrossOrigin(origins = "*")public SseEmitter subscribe(@PathVariable String id) {SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);sseEmitterStore.addEmitter(id,emitter);emitter.onCompletion(() -> sseEmitterStore.removeEmitter(id));emitter.onError(e -> sseEmitterStore.removeEmitter(id));return emitter;}@GetMapping("/unbind/{id}")@CrossOrigin(origins = "*")public ServerResponse deleteItem(@PathVariable String id) {this.sseEmitterStore.removeEmitter(id);return ServerResponse.success(true);}
}

异步发送消息service

    @Asyncpublic void broadcastMessage(String message) {List<String> keysToDelete = new ArrayList<>();this.sseEmitterStore.getEmitters().forEach((k, v) -> {try {v.send(message);} catch (Throwable t) {keysToDelete.add(k);}});keysToDelete.forEach(this.sseEmitterStore::removeEmitter);}

2、无法序列化的问题

跑起来之后,结果报错

DefaultSerializer requires a Serializable payload but received an object of type [org.springframework.web.servlet.mvc.method.annotation.SseEmitter]

错误信息已经很明显了

因为 SseEmitter 并不是一个实现了 Serializable 接口的类,因此不能被默认的序列化器正确处理。

问了AI

        

3、解决无法序列化问题

3.1自定义redis自定义序列化器

public class CustomJackson2JsonRedisSerializer<T> implements RedisSerializer<T> {private static final long serialVersionUID = -7649863253433761554L;private final ObjectMapper objectMapper;public CustomJackson2JsonRedisSerializer() {this.objectMapper = new ObjectMapper();this.objectMapper.registerModule(new JavaTimeModule());this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);}@Overridepublic byte[] serialize(T t) throws SerializationException {if (t == null) {return new byte[0];}try {return objectMapper.writeValueAsBytes(t);} catch (JsonProcessingException e) {throw new SerializationException("Could not write JSON: " + e.getMessage(), e);}}@Overridepublic T deserialize(byte[] bytes) throws SerializationException {if (bytes == null || bytes.length == 0) {return null;}try {return (T) objectMapper.readValue(bytes, SseEmitter.class);} catch (IOException e) {throw new SerializationException("Could not read JSON: " + e.getMessage(), e);}}
}

3.2redis配置,使序列化器生效

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);StringRedisSerializer stringSerializer = new StringRedisSerializer();CustomJackson2JsonRedisSerializer<Object> jacksonSerializer = new CustomJackson2JsonRedisSerializer<>();// 根据实际情况,自行修改template.setKeySerializer(stringSerializer);template.setValueSerializer(jacksonSerializer);template.setHashKeySerializer(stringSerializer);template.setHashValueSerializer(jacksonSerializer);template.afterPropertiesSet();return template;}
}

再次启动服务,即生效。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • unity Android + WebGL 浏览器打开
  • 【开源社区】Elasticsearch(ES)中 exists 查询空值字段的坑
  • 【前端】vue监视属性和计算属性对比
  • mp4怎么转换成mp3?看了就会的8种mp4转mp3方法!
  • 快速查找数组中出现奇数次的数字
  • Web应用加密数据传输方案
  • mac安装xmind
  • 前后端不分离,form前端表单提交,springboot后端list接收
  • <数据集>车内视角行人识别数据集<目标检测>
  • Ubuntu系统入门
  • fpga图像处理实战-水平镜像
  • Linux--网络层 IP协议
  • 数据结构-双向链表 代码实现
  • 二刷代码随想录训练营Day 38|322. 零钱兑换、279.完全平方数、139.单词拆分
  • 证书学习(一)keytool 工具使用介绍
  • 【Leetcode】104. 二叉树的最大深度
  • AHK 中 = 和 == 等比较运算符的用法
  • angular2 简述
  • Angular4 模板式表单用法以及验证
  • Docker 笔记(2):Dockerfile
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Median of Two Sorted Arrays
  • mysql 5.6 原生Online DDL解析
  • Mysql优化
  • Nodejs和JavaWeb协助开发
  • ReactNative开发常用的三方模块
  • Redis字符串类型内部编码剖析
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • springMvc学习笔记(2)
  • 浮动相关
  • 关于使用markdown的方法(引自CSDN教程)
  • 京东美团研发面经
  • 实现简单的正则表达式引擎
  • 王永庆:技术创新改变教育未来
  • 我的业余项目总结
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​【已解决】npm install​卡主不动的情况
  • #NOIP 2014# day.2 T2 寻找道路
  • (SERIES12)DM性能优化
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (二十三)Flask之高频面试点
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (六)Flink 窗口计算
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (十三)Flask之特殊装饰器详解
  • (一)为什么要选择C++
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • .Net 8.0 新的变化
  • .NET Core 中插件式开发实现
  • .net core使用ef 6
  • .NET LINQ 通常分 Syntax Query 和Syntax Method