SpringBoot / Vue 对SSE的基本使用(简单上手)

这篇具有很好参考价值的文章主要介绍了SpringBoot / Vue 对SSE的基本使用(简单上手)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、SSE是什么?

SSE技术是基于单工通信模式,只是单纯的客户端向服务端发送请求,服务端不会主动发送给客户端。服务端采取的策略是抓住这个请求不放,等数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求,周而复始。

注意:因为EventSource对象是SSE的客户端,可能会有浏览器对其不支持

二、sse 与 websoket

SSE(Server-Sent Events)

是 HTML5 遵循 W3C 标准提出的客户端和服务端之间进行实时通信的协议。

优点

  • SSE 客户端可以接收来自服务器的“流”数据,而不需要进行轮询。由于没有浪费的请求,因此 SSE 对于减轻服务器的压力非常有用。
  • SSE 使用纯 JavaScript 实现简单,不需要额外的插件或库来处理消息。客户端可以使用 EventSource 接口轻松地与 SSE 服务器通信。
  • SSE 天生具有自适应性,由于 SSE 是基于 HTTP 响应使用 EventStream 传递消息,因此它利用了 HTTP 的开销和互联网上的结构。
  • SSE 可以与任何服务器语言和平台一起使用,因为 SSE 是一种规定了消息传递方式的技术,不依赖于具体的服务器语言和平台。

缺点

  • SSE 是单向通信,只能从服务器推送到客户端。如果应用程序需要双向通信,就需要使用 Websocket。
  • SSE无法发送二进制数据,只能发送 UTF-8 编码的文本。如果应用程序需要发送二进制数据,就需要使用 Websocket。
  • SSE 不是所有浏览器都支持。虽然 SSE 是 HTML5 的一部分,但具体的浏览器支持性可能会有差异。

Websocket

是 HTML5 的一部分,提供了一种双向通信的机制。

优点

  • Websocket 支持双向通信。使用 Websocket 可以同时向客户端发送和接收数据。
  • Websocket 协议可以传输二进制数据,这使得 Websocket 更加灵活和强大。
  • Websocket 连接长期存在,而不需要仅仅为了接收数据而保持 HTTP 连接打开。
  • Websocket 的实现支持跨域的通信,可以方便地进行跨域通信。

缺点

  • Websocket 不支持所有浏览器。特别是老浏览器可能不支持 Websocket 协议。
  • Websocket 是一种全双工的通信方式。由于 Websocket 长期存在,会占用服务器资源。在高并发场景下,应该考虑使用 SSE。

三、前端示例代码:

// 建立连接
 createSseConnect(clientId){
    if(window.EventSource){
        const eventSource = new EventSource('http://127.0.0.1:8083/sse/createSseConnect?clientId='+clientId);
        console.log(eventSource)
        
        eventSource.onmessage = (event) =>{
            console.log("onmessage:"+clientId+": "+event.data)
        };
        
        eventSource.onopen = (event) =>{
            console.log("onopen:"+clientId+": "+event)
        };
        
        eventSource.onerror = (event) =>{
            console.log("onerror :"+clientId+": "+event)
        };
        
        eventSource.close = (event) =>{
            console.log("close :"+clientId+": "+event)
        };

    }else{
        console.log("你的浏览器不支持SSE~")
    }
    console.log(" 测试 打印")
},

四、后端示例代码:

SseController

package com.joker.cloud.linserver.controller;

import com.joker.cloud.linserver.conf.sse.sseUtils;
import com.joker.common.message.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;

/**
 * SseController
 *
 * @author joker
 * @version 1.0
 * 2023/8/9 11:18
 **/
@RestController
@Slf4j
@CrossOrigin
@RequestMapping("/sse")
public class SseController {

    @Autowired
    private sseUtils sseUtils;


    @GetMapping(value = "/createSseConnect", produces="text/event-stream;charset=UTF-8")
    public SseEmitter createSseConnect(@RequestParam(name = "clientId", required = false) Long clientId) {
        return sseUtils.connect(clientId);
    }


    @PostMapping("/sendMessage")
    public void sendMessage(@RequestParam("clientId") Long clientId, @RequestParam("message")  String message){
        sseUtils.sendMessage(clientId, "123456789", message);
    }

    @GetMapping(value = "/listSseConnect")
    public Result<Map<Long, SseEmitter>> listSseConnect(){
        Map<Long, SseEmitter> sseEmitterMap = sseUtils.listSseConnect();
        return Result.success(sseEmitterMap);
    }


    /**
     * 关闭SSE连接
     *
     * @param clientId 客户端ID
     **/
    @GetMapping("/closeSseConnect")
    public Result closeSseConnect(Long clientId) {
        sseUtils.deleteUser(clientId);
        return Result.success();
    }

}

sseUtils工具类

package com.joker.cloud.linserver.conf.sse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;


/**
 * sseUtils
 *
 * @author joker
 * @version 1.0
 * 2023/8/9 11:20
 **/
@Slf4j
@Component
public class sseUtils {

    private static final Map<Long, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();

    /**
     * 创建连接
     */
    public SseEmitter connect(Long userId) {
        if (sseEmitterMap.containsKey(userId)) {
            SseEmitter sseEmitter =sseEmitterMap.get(userId);
            sseEmitterMap.remove(userId);
            sseEmitter.complete();
        }
        try {
            UUID uuid = UUID.randomUUID();
            String str = uuid.toString();
            String temp = str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);

            // 设置超时时间,0表示不过期。默认30秒
            SseEmitter sseEmitter = new SseEmitter(30*1000L);
            sseEmitter.send(SseEmitter.event().id(temp).data(""));
//            reconnectTime(10*1000L)
            // 注册回调
            sseEmitter.onCompletion(completionCallBack(userId));
//            sseEmitter.completeWithError(errorCallBack(userId));
            sseEmitter.onTimeout(timeoutCallBack(userId));
            sseEmitterMap.put(userId, sseEmitter);
            log.info("创建sse连接完成,当前用户:{}", userId);
            return sseEmitter;
        } catch (Exception e) {
            log.info("创建sse连接异常,当前用户:{}", userId);
        }
        return null;
    }

    /**
     * 给指定用户发送消息
     *
     */
    public boolean sendMessage(Long userId,String messageId, String message) {
        if (sseEmitterMap.containsKey(userId)) {
            SseEmitter sseEmitter = sseEmitterMap.get(userId);
            try {
                sseEmitter.send(SseEmitter.event().id(messageId).data(message));
//                reconnectTime(10*1000L)
                log.info("用户{},消息id:{},推送成功:{}", userId,messageId, message);
                return true;
            }catch (Exception e) {
                sseEmitterMap.remove(userId);
                log.info("用户{},消息id:{},推送异常:{}", userId,messageId, e.getMessage());
                sseEmitter.complete();
                return false;
            }
        }else {
            log.info("用户{}未上线", userId);
        }
        return false;
    }

    /**
     * 删除连接
     * @param userId
     */
    public void deleteUser(Long userId){
        removeUser(userId);
    }

    private static Runnable completionCallBack(Long userId) {
        return () -> {
            log.info("结束sse用户连接:{}", userId);
            removeUser(userId);
        };
    }

    private static Throwable errorCallBack(Long userId) {
        log.info("sse用户连接异常:{}", userId);
        removeUser(userId);
        return new Throwable();
    }

    private static Runnable timeoutCallBack(Long userId) {
        return () -> {
            log.info("连接sse用户超时:{}", userId);
            removeUser(userId);
        };
    }

    /**
     * 断开
     * @param userId
     */
    public static void removeUser(Long userId){
        if (sseEmitterMap.containsKey(userId)) {
            SseEmitter sseEmitter = sseEmitterMap.get(userId);
            sseEmitterMap.remove(userId);
            sseEmitter.complete();
        }else {
            log.info("用户{} 连接已关闭",userId);
        }
    }

    public Map<Long, SseEmitter> listSseConnect(){
         return sseEmitterMap;
    }
}

五、模拟测试:

模拟浏览器发送建立连接的请求:

SpringBoot / Vue 对SSE的基本使用(简单上手),开发及业务相关问题,前端相关,JAVA开发,spring boot,vue.js,后端,websocket

切换到时间栏目,可以看到长连接始终保持着的:

SpringBoot / Vue 对SSE的基本使用(简单上手),开发及业务相关问题,前端相关,JAVA开发,spring boot,vue.js,后端,websocket

切换到eventStream:可以看到后端通信的streams流数据

SpringBoot / Vue 对SSE的基本使用(简单上手),开发及业务相关问题,前端相关,JAVA开发,spring boot,vue.js,后端,websocket

使用postMan 模拟后端服务器推送给客户端消息

SpringBoot / Vue 对SSE的基本使用(简单上手),开发及业务相关问题,前端相关,JAVA开发,spring boot,vue.js,后端,websocket

浏览器建立的连接中会看到服务器推送到客户端的消息内容及ID等基础信息

SpringBoot / Vue 对SSE的基本使用(简单上手),开发及业务相关问题,前端相关,JAVA开发,spring boot,vue.js,后端,websocket

控制台也可以监听到事件的变化并输出

SpringBoot / Vue 对SSE的基本使用(简单上手),开发及业务相关问题,前端相关,JAVA开发,spring boot,vue.js,后端,websocket文章来源地址https://www.toymoban.com/news/detail-753237.html

到了这里,关于SpringBoot / Vue 对SSE的基本使用(简单上手)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Vue】Vue的简单介绍与基本使用

    Vue是一款用于 构建用户界面 的 JavaScript 框架。 它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套 声明式的、组件化 的编程模型,帮助你高效地开发用户界面。 无论是简单还是复杂的界面,Vue 都可以胜任。 传统方式 vue方式 使用 vue 构建用户界面,解决了jQuery + 模板引擎

    2024年02月04日
    浏览(38)
  • SpringBoot使用SSE进行实时通知前端

    项目有个需求是要实时通知前端,告诉前端这个任务加载好了。然后想了2个方案,一种是用websocket进行长连接,一种是使用SSE(Sever Send Event),是HTTP协议中的一种,Content-Type为text/event-stream,能够保持长连接。 websocket是前端既能向后端发送消息,后端也能向前端发送消息。

    2024年02月08日
    浏览(45)
  • SpringBoot实用开发(十二)-- MongoDB语句的简单使用

    目录 1.新增文档 2.删除文档 3.修改文档 4.查询文档 4.1 基础查询 4.2 条件

    2024年01月24日
    浏览(28)
  • 在vue2中使用SSE(服务器发送事件)

    SSE(Server-Sent Events,服务器发送事件)是围绕只读Comet 交互推出的API 或者模式。 SSE API允许网页获得来自服务器的更新,用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。服务器响应的MIME类型必须是text/event-stream,而且是浏览器中的JavaScript API 能

    2024年02月11日
    浏览(37)
  • springboot项目开发,使用thymeleaf前端框架的简单案例

    springboot项目开发,使用thymeleaf前端框架的简单案例!我们看一下,如何在springboot项目里面简单的构建一个thymeleaf的前端页面。来完成动态数据的渲染效果。 第一步,我们在上一小节,已经提前预下载了对应的组件了。 如图,springboot的强大之处就在于,它有一套完整的版本依

    2024年01月25日
    浏览(29)
  • SpringBoot自定义starter开发:IP计数业务功能开发

    🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎 📚系列专栏:Java全栈,计算机系列(火速更新中) 💭 格言:种一棵树最好的时间是十年前,其次是现在 🏡动动小手,点个关注不迷路,感

    2024年04月16日
    浏览(24)
  • MybatisPlus基本使用(MP快速上手)

    目录 概述 MP快速上手(基本操作)  SpringBoot中MyBatisPlus环境搭建 增删改查 分页 条件查询(聚合,模糊,匹配范围) 条件:范围匹配 (大于小于等于...) 优化查询条件书写() 条件:查询投影 条件:模糊查询(非全文检索)  条件:聚合函数和分组 条件:排序操作   

    2024年02月05日
    浏览(24)
  • Flask 快速上手教程 — 了解与基本使用

    这篇博客是我刚接触 flask,研究文档时的一些记录与体会,希望对各位刚接触 flask 的朋友有所帮助。 且在此篇后,我还会另写一篇关于纯后端的 flask 教程,介绍一下如何使用 flask 创建一个较为完善的纯后端的服务。 在阅读下面的内容前,读者应该具有基础的python知识,且

    2024年02月09日
    浏览(33)
  • 瑞芯微RK3588开发板的固件烧录完整教程(简单好上手)

    ​​​​​​​本期技术干货内容分享嵌入式开发板固件烧录教程,以英码嵌入式开发板EVM3588为例,该发板搭载的是瑞芯微RK3588平台,烧录方式采用最常用的USB_OTG烧录,简单又方便! 开发环境 主机:Ubuntu 20.04 开发板:英码科技EVM3588开发板 烧录工具:RKDevTool_Release_v2.92.zi

    2024年02月11日
    浏览(37)
  • 【一文到底】【0基础】【快速上手】Django基本使用

    和之前python一样,通过pip来安装即可 django和其他第三方Python模块一样,会在当前python环境下的 libsite-package 中,只是django是比较大的那种模块。 But,django这个包呢同时会生成 django-admin.exe 在 Scripts 文件夹中,这个exe可执行文件是帮助我们操作django项目的。目录情况大体如下:

    2023年04月09日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包