持久化SSE对象

2024-08-24 09:36
文章标签 对象 持久 sse

本文主要是介绍持久化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;}
}

再次启动服务,即生效。

这篇关于持久化SSE对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102107

相关文章

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

golang 对象池sync.Pool的实现

《golang对象池sync.Pool的实现》:本文主要介绍golang对象池sync.Pool的实现,用于缓存和复用临时对象,以减少内存分配和垃圾回收的压力,下面就来介绍一下,感兴趣的可以了解... 目录sync.Pool的用法原理sync.Pool 的使用示例sync.Pool 的使用场景注意sync.

SpringBoot项目中Redis存储Session对象序列化处理

《SpringBoot项目中Redis存储Session对象序列化处理》在SpringBoot项目中使用Redis存储Session时,对象的序列化和反序列化是关键步骤,下面我们就来讲讲如何在Spri... 目录一、为什么需要序列化处理二、Spring Boot 集成 Redis 存储 Session2.1

Java实例化对象的​7种方式详解

《Java实例化对象的​7种方式详解》在Java中,实例化对象的方式有多种,具体取决于场景需求和设计模式,本文整理了7种常用的方法,文中的示例代码讲解详细,有需要的可以了解下... 目录1. ​new 关键字(直接构造)​2. ​反射(Reflection)​​3. ​克隆(Clone)​​4. ​反序列化

Spring AI 实现 STDIO和SSE MCP Server的过程详解

《SpringAI实现STDIO和SSEMCPServer的过程详解》STDIO方式是基于进程间通信,MCPClient和MCPServer运行在同一主机,主要用于本地集成、命令行工具等场景... 目录Spring AI 实现 STDIO和SSE MCP Server1.新建Spring Boot项目2.a

C++类和对象之初始化列表的使用方式

《C++类和对象之初始化列表的使用方式》:本文主要介绍C++类和对象之初始化列表的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C++初始化列表详解:性能优化与正确实践什么是初始化列表?初始化列表的三大核心作用1. 性能优化:避免不必要的赋值操作2. 强