Spring Boot中的SSE与缓存集成:使用Redis加速事件推送

news/2024/9/24 12:25:30/

Spring Boot中的SSE与缓存集成:使用Redis加速事件推送

实时事件推送在现代Web应用中变得越来越重要,而Spring Server-Sent Events(SSE)为实现实时推送提供了一种简单而有效的方式。然而,随着应用规模的增长,实时事件推送可能会面临性能瓶颈。为了解决这个问题,我们可以考虑将Spring Boot中的SSE与缓存(如Redis)集成,以加速事件推送,提高系统的性能和可扩展性。

基础SSE配置

首先,我们需要建立基础的Spring Boot SSE配置。我们可以使用SseEmitter来处理SSE请求,并通过WebSocket进行实时事件推送。

@RestController
public class SSEController {@Autowiredprivate SimpMessageSendingOperations messagingTemplate;private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();@GetMapping("/sse/{clientId}")public SseEmitter handleSSE(@PathVariable String clientId) {SseEmitter emitter = new SseEmitter();// 处理连接关闭时的清理逻辑emitter.onCompletion(() -> cleanupEmitter(clientId));// 添加到emitters集合emitters.put(clientId, emitter);return emitter;}private void cleanupEmitter(String clientId) {// 清理资源,例如从emitters集合中移除对应的emitteremitters.remove(clientId);}// 其他处理SSE请求的逻辑...
}

在上述代码中,我们创建了一个SseEmitter,并在onCompletion回调中定义了连接关闭时的清理逻辑。emitters集合用于存储每个客户端的SseEmitter对象,以便后续进行事件推送。

缓存与事件推送集成

现在,让我们将缓存(Redis)集成到SSE事件推送中,以提高性能。

1 、集成Redis作为缓存

首先,我们需要在Spring Boot项目中集成Redis作为缓存。可以通过在pom.xml文件中添加相应的依赖,以及在application.properties中配置Redis连接信息来实现。

<!-- pom.xml -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# application.properties
spring.redis.host=127.0.0.1
spring.redis.port=6379

2 、使用Redis进行事件推送

在SSE事件推送中,我们可以使用Redis的消息发布/订阅机制。每当有新的实时事件产生时,我们将事件发送到Redis的某个频道,然后所有订阅了该频道的客户端都会接收到相应的事件。

@Component
public class RedisEventPublisher {@Autowiredprivate SimpMessageSendingOperations messagingTemplate;@Autowiredprivate StringRedisTemplate stringRedisTemplate;public void publishEvent(String clientId, Event event) {// 将事件序列化为JSON字符串String eventJson = convertEventToJson(event);// 发布事件到Redis频道stringRedisTemplate.convertAndSend("sse-events:" + clientId, eventJson);}private String convertEventToJson(Event event) {// 将事件对象转换为JSON字符串// 使用Jackson等库进行JSON序列化// ...return jsonString;}
}

在上述代码中,RedisEventPublisher负责将事件发布到Redis频道。每个客户端的频道名由"sse-events:" + clientId构成,确保每个客户端都有一个独立的频道。

3 、处理Redis频道消息并推送事件

我们需要编写一个监听器来处理Redis频道的消息,并将事件推送给相应的客户端。

@Component
public class RedisMessageListener {@Autowiredprivate SimpMessageSendingOperations messagingTemplate;@MessageMapping("/subscribe")public void subscribe(@Payload String clientId, SimpMessageHeaderAccessor headerAccessor) {// 订阅Redis频道stringRedisTemplate.execute((RedisCallback<Void>) connection -> {connection.subscribe(new MessageListener() {@Overridepublic void onMessage(Message message, byte[] pattern) {// 处理接收到的事件消息String eventJson = new String(message.getBody(), StandardCharsets.UTF_8);Event event = convertJsonToEvent(eventJson);// 将事件推送给客户端messagingTemplate.convertAndSendToUser(clientId, "/queue/sse", event);}}, ("sse-events:" + clientId).getBytes(StandardCharsets.UTF_8));return null;});}private Event convertJsonToEvent(String eventJson) {// 将JSON字符串转换为事件对象// 使用Jackson等库进行JSON反序列化// ...return event;}
}

在上述代码中,一RedisMessageListener通过@MessageMapping("/subscribe")监听客户端订阅事件。当客户端订阅时,它会订阅对应的Redis频道,并在接收到频道

消息时处理事件,并使用
SimpMessageSendingOperations将事件推送给客户端。

性能考虑与优化

为了确保性能和可扩展性,我们可以考虑以下几个方面的优化:

1 、事件批处理

当有多个事件需要推送时,可以考虑批处理事件而不是逐个推送。通过使用stringRedisTemplate的convertAndSend方法,我们可以将多个事件以列表的形式一次性发送到Redis频道,减少网络开销。

public void publishEvents(String clientId, List<Event> events) {// 将事件列表转换为JSON字符串列表List<String> eventJsonList = events.stream().map(this::convertEventToJson).collect(Collectors.toList());// 一次性发送事件列表到Redis频道stringRedisTemplate.convertAndSend("sse-events:" + clientId, eventJsonList);
}

2 、事件过滤

在事件推送之前,可以考虑对事件进行过滤,只推送客户端感兴趣的事件。这可以通过在subscribe方法中增加逻辑来实现。

public void subscribe(@Payload String clientId, SimpMessageHeaderAccessor headerAccessor) {// 订阅Redis频道,仅接收客户端感兴趣的事件stringRedisTemplate.execute((RedisCallback<Void>) connection -> {connection.subscribe(new MessageListener() {@Overridepublic void onMessage(Message message, byte[] pattern) {// 处理接收到的事件消息String eventJson = new String(message.getBody(), StandardCharsets.UTF_8);Event event = convertJsonToEvent(eventJson);// 仅推送客户端感兴趣的事件if (isInterested(clientId, event)) {messagingTemplate.convertAndSendToUser(clientId, "/queue/sse", event);}}}, ("sse-events:" + clientId).getBytes(StandardCharsets.UTF_8));return null;});
}private boolean isInterested(String clientId, Event event) {// 判断客户端是否对该事件感兴趣// ...return true;
}

通过增加isInterested方法,我们可以根据具体业务逻辑判断客户端是否对某个事件感兴趣,从而减少不必要的事件推送。

总 结

通过将Spring Boot中的SSE与缓存(Redis)集成,我们实现了实时事件推送的性能优化。通过使用Redis作为事件缓存,我们可以加速事件推送,提高系统的性能和可扩展性。在优化过程中,我们考虑了事件批处理、事件过滤等方面,以确保推送的事件是高效且符合客户端需求的。这种集成方式不仅能够提升用户体验,而且在大规模应用中具有良好的性能表现。


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

相关文章

C++ | Leetcode C++题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isMatch(string s, string p) {auto allStars [](const string& str, int left, int right) {for (int i left; i < right; i) {if (str[i] ! *) {return false;}}return true;};auto charMatch []…

安装系统出现dracut-initqueue状态

如图显示&#xff0c;系统安装时未找到/dev/root位置 输入blkid&#xff0c;查看centos系统所在的盘 重启&#xff0c;按e进入 将linuxefi /images/pxeboot/vmlinuz inst.stage2hd:LABELCentOS\x207\x20x86_64 quiet 改成inst.stage2hd:/dev/sdb4 quiet (改成blkid中的盘符名称…

Oracle進階SQLDay03

一、函數進階復習 1、行轉列 select 用水儿量&#xff08;噸&#xff09; 统计项, sum(case when t_account.month01 then USENUM end) 一月, sum(case when t_account.month02 then USENUM end) 二月, sum(case when t_account.month03 then USENUM end) 三月, sum(case when …

Spring 事务 @Transactional 注解

上期我们讲解了Spring事务的两种实现&#xff0c;其中声明式注解使用了 Transactional 注解&#xff0c; 接下来我们学习 该注解的使用细节。 我们主要学习 Transactional注解当中的三个常见属性&#xff1a; rollbackFor&#xff1a;异常回滚属性&#xff0c;指定能够出发事…

宝塔面板使用docker+nginx+gunicorn部署Django项目实战教程

第一步&#xff1a;创建Django项目 使用pip install django安装创建django项目的依赖在电脑某个根目录下执行django-admin startproject app创建一个名为app的Django项目。目录结构如下: ├── app │ ├── init.py │ ├── asgi.py │ ├── settings.py │ ├── url…

redis中的双写一致性问题

双写一致性问题 1.先删除缓存或者先修改数据库都可能出现脏数据。 2.删除两次缓存&#xff0c;可以在一定程度上降低脏数据的出现。 3.延时是因为数据库一般采用主从分离&#xff0c;读写分离。延迟一会是让主节点把数据同步到从节点。 1.读写锁保证数据的强一致性 因为一般放…

Django模型的继承

Django模型的继承 Django模型的继承&#xff0c;包括模型的抽象基类、Meta继承、related_name和related_query_name属性、多表继承、Meta和多表继承、继承与反向关系、代理模型、代理模型继承和未托管模型&#xff0c;以及多重继承等内容。本文讲解一下抽象基类继承&#xff0…

springcloud Ribbon的详解

1、Ribbon是什么 Ribbon是Netflix发布的开源项目&#xff0c;Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的框架。 2、Ribbon能干什么 LB负载均衡(Load Balance)是什么&#xff1f;简单的说就是将用户的请求平摊的分配到多个服务上&#xff0c;从而达…