Spring SseEmitter推送消息及常用方法

 更新时间:2024年07月16日 10:54:43   作者:way_more  
SseEmitter 是 Spring Framework 提供的用于支持 Server-Sent Events(SSE)的类,它允许服务器端向客户端推送事件流,实现服务器到客户端的单向通信,这篇文章主要介绍了Spring SseEmitter推送消息,需要的朋友可以参考下

SseEmitter

SseEmitter 是 Spring Framework 提供的用于支持 Server-Sent Events(SSE)的类,它允许服务器端向客户端推送事件流,实现服务器到客户端的单向通信。

下面我们来看一下SSE的介绍

SSE(Server-Sent Events,服务器推送事件)是一种用于在客户端和服务器之间建立单向通信的技术。它允许服务器端发送异步消息给客户端,而无需客户端明确地请求数据。SSE 基于 HTTP 协议,使用简单的格式来传输文本数据,通常被用于实时通知、实时更新等场景。

我们知道推送消息还有另一种方式是webscoket,那么SSE和webscoket有什么区别?

  • 协议:SSE 基于 HTTP 协议,而 WebSocket 则是一种独立的协议。SSE 使用简单的文本格式传输数据,而 WebSocket 使用二进制帧进行通信。
  • 双向通信:WebSocket 支持双向通信,客户端和服务器可以同时发送和接收数据。而 SSE 是单向通信,只允许服务器向客户端推送数据,客户端无法发送数据给服务器。
  • 连接状态:WebSocket 使用长连接,保持持久连接,可以实现实时双向通信。而 SSE 是基于短连接,每次请求结束后需要重新建立连接。
  • 兼容性:WebSocket 在大多数现代浏览器中都有良好的支持,但在一些旧版本的浏览器中可能会存在兼容性问题。SSE 在较新的浏览器中也有良好的支持,但在旧版本浏览器中可能不被支持。
  • 使用场景:WebSocket 适用于需要实时双向通信的场景,如聊天应用、实时游戏等。SSE 更适合服务器向客户端单向推送实时数据的场景,如股票报价、实时通知等。

综上所述,SSE 和 WebSocket 都是实现实时通信的技术,但在协议、双向通信、连接状态、兼容性和使用场景等方面存在一些区别。选择使用哪种技术取决于具体的需求和应用场景。

SseEmitter 常用方法

下面是 SseEmitter 类的一些常用方法

SseEmitter():构造方法,用于创建一个新的 SseEmitter 对象。

SseEmitter(Long timeout):构造方法,用于创建一个设置了超时时间新的 SseEmitter 对象。

send(Object data):直接发送一个带有默认事件名称的 SSE 事件给客户端,数据由参数指定。

send(Object data, MediaType mediaType):发送一个带有指定媒体类型的 SSE 事件给客户端。

onTimeout(Runnable callback):设置连接超时时的回调函数。

onCompletion(Runnable callback):设置连接完成时的回调函数。

onError(Consumer callback):设置发送错误时的回调函数。

complete():表示 SSE 事件流结束,通知客户端不再有新的事件发送。

SseEmitter推送消息工具类

下面,我就用SseEmitter来编写一个推送消息给所有在线用户和某个用户的工具类

@Slf4j
@Component
public class SseEmitterUtil {
    //保存客户连接
    public static Map<String, SseEmitter> userSseEmitters = new HashMap<>();
    /**
     * 用户上线,开启一个SSE连接
     * @param userId
     * @return
     */
    public SseEmitter subscribe(String userId){
        //创建一个超时时间为30秒的SseEmitter对象
        SseEmitter sseEmitter = new SseEmitter(30*1000L);
        //设置回调函数
        sseEmitter.onCompletion(completionCallBack(userId));
        sseEmitter.onError(errorCallBack(userId));
        sseEmitter.onTimeout(timeoutCallBack(userId));
        //缓存起来
        userSseEmitters.put(userId,sseEmitter);
        return sseEmitter;
    }
    /**
     * 推送消息给某个客户端
     * @param userId  用户ID
     * @param content 消息体
     */
    public static void push(String userId,String content){
        SseEmitter sseEmitter = userSseEmitters.get(userId);
        if (sseEmitter != null) {
            try {
                sseEmitter.send(content);
            }catch (Exception e){
                log.error("用户-{} 推送消息异常:{}",userId,e);
            }
        }else {
            log.info("用户-{} 连接不存在",userId);
        }
    }
    /**
     * 推送消息给所有客户端
     * @param content 消息体
     */
    public static void pushAllUser(String content){
        for (SseEmitter emitter : userSseEmitters.values()) {
            try {
                emitter.send(SseEmitter.event().name("全局消息").data(content));
            } catch (Exception e) {
                log.error("推送消息异常:{}",e);
            }
        }
    }
    /**
     * 删除某个客户连接
     * @param userId
     */
    public static void removeEmitter(String userId) {
        SseEmitter sseEmitter = userSseEmitters.get(userId);
        if (sseEmitter != null) {
            sseEmitter.complete();
            userSseEmitters.remove(userId);
        }else {
            log.info("用户-{} 连接不存在",userId);
        }
    }
    private Runnable completionCallBack(String userId) {
        return () -> {
            log.info("用户-{} 连接成功", userId);
        };
    }
    /**
     * 出现超时,将当前用户缓存删除
     * @param userId
     * @return
     */
    private Runnable timeoutCallBack(String userId) {
        return () -> {
            log.info("用户-{} 连接超时", userId);
            userSseEmitters.remove(userId);
        };
    }
    /**
     * 出现异常,将当前用户缓存删除
     * @param userId
     * @return
     */
    private Consumer<Throwable> errorCallBack(String userId) {
        return throwable -> {
            log.info("用户-{} 连接异常", userId);
            userSseEmitters.remove(userId);
        };
    }
}

我们在登录的时候就可以调用subscribe来建立连接,然后发在线用户公告就可以使用pushAllUser,某个用户通知就使用push

SseEmitter搭配监听器

我们还可以搭配监听器来使用,定义某个事件的监听器,当事件发生,就使用SseService 来推送消息

如下,我们定义一个监听器,用于监听用户登录的事件。在监听器中,使用 SseService 向所有在线用户发送一条上线通知。

@Component
public class UserLoginListener {
   private final SseEmitterUtil sseService;
    @Autowired
    public UserLoginListener(SseEmitterUtil sseService) {
        this.sseService = sseService;
    }
    @EventListener
    public void onUserLogin(UserLoginEvent event) {
        String message = "用户 " + event.getUserId() + " 上线了";
        sseService.pushAllUser(message);
    }
}

当用户登录时,发布一个 UserLoginEvent 事件,触发 UserLoginListener 中的 onUserLogin 方法,向所有在线用户发送上线通知。

@Service
public class UserService {
    private final ApplicationEventPublisher publisher;
    @Autowired
    public UserService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
    public void login(String userId) {
        // 登录逻辑
        publisher.publishEvent(new UserLoginEvent(userId));
    }
}

事件监听除了可以使用spring的监听机制,还可以使用redis的事件监听,这个之后讲解

到此这篇关于Spring SseEmitter推送消息的文章就介绍到这了,更多相关Spring SseEmitter推送消息内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Bean创建和循环依赖

    Spring Bean创建和循环依赖

    这篇文章主要介绍了Spring Bean创建和循环依赖,讲述了Spring容器中 Bean 的创建过程已经主要的方法,另外也着重分析了循环依赖的问题,需要的小伙伴可以参考一下
    2022-05-05
  • SpringBoot 配置文件加密的步骤

    SpringBoot 配置文件加密的步骤

    这篇文章主要介绍了SpringBoot 配置文件加密的步骤,帮助大家更好的理解和学习使用springboot框架,感兴趣的朋友可以了解下
    2021-03-03
  • 谈谈为JAXB和response设置编码,解决wechat4j中文乱码的问题

    谈谈为JAXB和response设置编码,解决wechat4j中文乱码的问题

    中文乱码是每个程序员都会遇到的问题,本篇文章主要介绍了谈谈为JAXB和response设置编码,解决wechat4j中文乱码的问题,具有一定的参考价值,有兴趣的可以了解一下。
    2016-12-12
  • Java实现雪花算法的示例代码

    Java实现雪花算法的示例代码

    SnowFlow算法是Twitter推出的分布式id生成算法,主要核心思想就是利用64bit的long类型的数字作为全局的id。本文将用Java语言实现雪花算法,感兴趣的可以学习一下
    2022-03-03
  • Java中List介绍及常见方法和五种遍历方式详解

    Java中List介绍及常见方法和五种遍历方式详解

    这篇文章主要给大家介绍了关于Java中List介绍及常见方法和五种遍历方式的相关资料,Java的List接口是集合框架的一部分,定义了一个有序的集合,允许存储重复的元素,并且可以通过索引访问这些元素,需要的朋友可以参考下
    2024-12-12
  • 一文搞明白Java Spring Boot分布式事务解决方案

    一文搞明白Java Spring Boot分布式事务解决方案

    这篇文章主要介绍了一文搞明白Java Spring Boot分布式事务解决方案,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • SpringBoot配置多数据源的四种方式分享

    SpringBoot配置多数据源的四种方式分享

    在日常开发中我们都是以单个数据库进行开发,在小型项目中是完全能够满足需求的,但是,当我们牵扯到大型项目的时候,单个数据库就难以承受用户的CRUD操作,那么此时,我们就需要使用多个数据源进行读写分离的操作,本文就给大家介绍SpringBoot配置多数据源的方式
    2023-07-07
  • Java关键字instanceof的两种用法实例

    Java关键字instanceof的两种用法实例

    这篇文章主要介绍了Java关键字instanceof的两种用法实例,本文给出了instanceof关键字用于判断一个引用类型变量所指向的对象是否是一个类(或接口、抽象类、父类)及用于数组比较,需要的朋友可以参考下
    2015-03-03
  • ehcache模糊批量移除缓存的方法

    ehcache模糊批量移除缓存的方法

    本篇文章主要介绍了ehcache模糊批量移除缓存的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • 深入理解java三种工厂模式

    深入理解java三种工厂模式

    下面小编就为大家带来一篇深入理解java三种工厂模式。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06

最新评论