SSE:后端向前端发送消息(springboot SseEmitter)

这篇具有很好参考价值的文章主要介绍了SSE:后端向前端发送消息(springboot SseEmitter)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

有一个项目,前端vue,后端springboot。现在需要做一个功能:用户在使用系统的时候,管理员发布公告,则使用系统的用户可以看到该公告。
基于此,一个简单的方案:前端使用JS方法setInterval,重复调用后端公告获取接口。此方法有几点缺陷:

  • 循环调用的时间间隔不好确定:太长了,获取公告的时效有延迟;太短了,给服务器造成压力,很多请求都是无用的(公告发布的时间不定,很可能几天都没有新公告);
  • token的续期问题:项目中,前端请求,需要带上token,token有过期时间,如果用户一直使用(前后端有交互),会无感续期。如果有这种定时循环和后端交互的场景,就会造成token用不过期(循环的调用会触发续期),当然,可以在续期中,排除某个场景的请求,但是这样的设计不好,因为这种场景太多了,就会造成维护上的困难。

因此就想到了,如果后端主动向前端推送消息,这个问题就可以完美解决。

方案

有两种方案可以实现后端向前端推送消息:

  1. 使用websocket;
  2. 使用sse;

这里介绍SSE的方式(如果系统中对这种消息的准确性和可靠性有严格的要求,则使用websocket,websocket的使用相对复杂的多);
如果想了解SSE的详细基础知识,可以参考阮一峰老师的这篇文章:Server-Sent Events 教程

SSE后端代码

SpringMVC中,已经集成了该功能,所以无需额外引入jar包,直接上代码:

@RestController
@RequestMapping("/notice")
public class NoticeController {

    @Autowired
    private NoticeService noticeService;

    @GetMapping(path = "createSseEmitter")
    public SseEmitter createSseEmitter(String id) {
        return noticeService.createSseEmitter(id);
    }

    @PostMapping(path = "sendMsg")
    public boolean sendMsg(String id, String content) {
        noticeService.sendMsg(id, content);
        return true;
    }

}

@Slf4j
@Service
public class NoticeServiceImpl implements NoticeService {
    @Autowired
    @Qualifier("sseEmitterCacheService")
    private CacheService<SseEmitter> sseEmitterCacheService;

    @Override
    public SseEmitter createSseEmitter(String clientId) {
        if (StringUtil.isBlank(clientId)) {
            clientId = UUID.randomUUID().toString().replace("-", "");
        }
        SseEmitter sseEmitter = sseEmitterCacheService.getCache(clientId);
        log.info("获取SSE,id={}", clientId);
        final String id = clientId;
        sseEmitter.onCompletion(() -> {
            log.info("SSE已完成,关闭连接 id={}", id);
            sseEmitterCacheService.deleteCache(id);
        });
        return sseEmitter;
    }
    @Override
    public void sendMsg(String clientId, String content) {
        if (sseEmitterCacheService.hasCache(clientId)) {
            SseEmitter sseEmitter = sseEmitterCacheService.getCache(clientId);
            try {
                sseEmitter.send(content);
            } catch (IOException e) {
                log.error("发送消息失败:{}", e.getMessage(), e);
                throw new BusinessRuntimeExcepption(CustomExcetionConstant.IO_ERR, "发送消息失败", e);
            }
        } else {
            log.error("SSE对象不存在");
            throw new BusinessRuntimeExcepption("SSE对象不存在");
        }
    }
}

这里,只列出了核心的代码,简而言之,需要做到两点即可:

  1. 前端首先是发起一个请求,创建SseEmitter,即createSseEmitter方法,该方法必须返回一个SseEmitter对象;
  2. 返回的SseEmitter,后端必须要缓存起来(我用的是ehcache,也可以直接定义一个map来缓存);

为什么要这么做?看下文,后端代码一起来分析就明白了。

前端代码

由于,我请求该接口,需要带上token,所以直接使用EventSource不行,另外这个IE也不支持。所以选择了一个工具:event-source-polyfill。

  1. 先安装event-source-polyfill
npm install event-source-polyfill
  1. 然后使用:
import { EventSourcePolyfill } from "event-source-polyfill";
  created() {
    let _this = this;
    this.source = new EventSourcePolyfill(
      "/" +
        process.env.VUE_APP_MANAGER_PRE_API_URL +
        "/notice/createSseEmitter?id=" +
        uuid(),
      {
        headers: {
          [process.env.VUE_APP_OAUTH_AUTHORIZATION]: store.getters.getToken,
        },
        //重连时间间隔,单位:毫秒,默认45000毫秒,这里设置为10分钟
        heartbeatTimeout: 10 * 60 * 1000,
      }
    );

    this.source.onopen = () => {
      console.log("NOTICE建立连接");
    };
    this.source.onmessage = (e) => {
      _this.scrollMessage = e.data;
      console.log("NOTICE接收到消息");
    };
    this.source.onerror = (e) => {
      if (e.readyState == EventSource.CLOSED) {
        console.log("NOTICE连接关闭");
      } else if (this.source.readyState == EventSource.CONNECTING) {
        console.log("NOTICE正在重连");
        //重新设置header
        this.source.headers = {
          [process.env.VUE_APP_OAUTH_AUTHORIZATION]: store.getters.getToken,
        };
      } else {
        console.log(e);
      }
    };
  },

有几点说明:

  • new EventSourcePolyfill中,可以带入header
  • heartbeatTimeout是一个心跳时间,默认情况下间隔heartbeatTimeout后,会触发重新连接后端接口;
  • this.source.headers,该行的作用是在重连的时候重新设置header,如果不这样,那么重连的时候,用的参数信息,还是和最开始的一样(包括本例中url中的id)。而由于我的项目中,如果token其他操作触发了刷新token,则有效token可能会变,所以,这里取缓存中放置的token,而不应该使用最初的token。
    好了,这样就基本实现了我们所需要的功能了。

特别注意

前端配置了代理,所以一直收不到后端发送的消息,尝试加入以下参数:

  devServer: {
    compress:false,
    …………
}

问题

之前在写后端的时候提到了两个问题:为什么要返回SseEmitter对象?为什么要缓存SseEmitter对象?
其实看过SSE的原理,都应该明白:这就是一个长连接,前端调用创建SseEmitter对象的接口,虽然接口返回了,但是并未结束(这就是为什么要返回SseEmitter对象,如果返回的是一个其他对象,就和普通的接口没两样了, 该接口就直接结束了),请看下截图:
sseemitter,WEB,VUE,SpringBoot,前端,spring boot,java,web
发起请求之后,一直是待处理,并未结束,10分钟之后,该请求被取消(前端设置的重连),然后重新发起连接,重新发起的连接也是在等待中。只有接收到消息后,这个请求的状态码才是200,但是这个时候才连接已经建立好了。其中的细节,这里不做讲述。
所以,如果再使用SseEmitter对象发送消息,则前端就可以收到对象的消息了(即实现后端向前端发送消息)。这里使用的SseEmitter对象,就是createSseEmitter接口返回的对象(也就是使用哪个SseEmitter对象,就可以向哪个前端发送消息)。这也就是为什么要缓存SseEmitter对象的原因了。

效果

通过调用发送消息接口,前端即可立即展示发送的消息:
sseemitter,WEB,VUE,SpringBoot,前端,spring boot,java,web文章来源地址https://www.toymoban.com/news/detail-788403.html

到了这里,关于SSE:后端向前端发送消息(springboot SseEmitter)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Java:SpringBoot整合WebSocket实现服务端向客户端推送消息

    思路: 后端通过websocket向前端推送消息,前端统一使用http协议接口向后端发送数据 本文仅放一部分重要的代码,完整代码可参看github仓库 websocket 前端测试 :http://www.easyswoole.com/wstool.html 依赖 项目目录 完整依赖 配置 WebSocketServer.java 前端页面 websocket.html 前端逻辑 index.js 参

    2024年02月04日
    浏览(36)
  • SSE与WebSocket分别实现服务器发送消息通知(Golang、Gin)

    服务端推送,也称为消息推送或通知推送,是一种允许应用服务器主动将信息发送到客户端的能力,为客户端提供了实时的信息更新和通知,增强了用户体验。 服务端推送的背景与需求主要基于以下几个诉求: 实时通知:在很多情况下,用户期望实时接收到应用的通知,如

    2024年02月03日
    浏览(37)
  • java socket Server TCP服务端向指定客户端发送消息;可查看、断开指定连接的客户端;以及设置客户端最大可连接数量。

    首先需要知道java里如何创建一个Socket服务器端。 提示:注意server.accept()方法调用会阻塞,只有新的客户端连接后才返回一个新的socket对象。如果一直未连接那么会一直处于阻塞状态 了解了如何创建一个socket服务器端后。那么如何实现给指定的连接客户端发送消息呢?首先我

    2024年02月11日
    浏览(46)
  • 获取第三方接口的EventStream返回给前端,SpringBoot+Vue+WebFlux+SseEmitter

    后端: 其中曾踩了一些坑: 1.原本是bodyToFlux(String.class),结果没有返回也没有报错,排查了许久,最终改成bodyToFlux(byte[].class) 2.原本未使用Mono.fromCallable进行异步请求,结果其实是一次获取到完整的流再给前端,与长连接实时输出信息相悖。实际应该把SseEmitter返回给前端,异

    2024年03月19日
    浏览(42)
  • 前端实现消息推送、即时通信、SSE、WebSocket、http简介

    服务端主动向客户端推送消息,使客户端能够即时接收到信息。 场景 页面接收到点赞,消息提醒 聊天功能 弹幕功能 实时更新数据功能 短轮询 浏览器(客户端)每隔一段时间向服务器发送http请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。 本质:客

    2024年02月16日
    浏览(31)
  • SSE实现消息实时推送,前端渐进式学习、实践,真香

    SSE(Server Sent Event),直译为服务器发送事件,顾名思义,也就是客户端可以获取到服务器发送的事件。我们常见的 http 交互方式是客户端发起请求,服务端响应,然后一次请求完毕;但是在 sse 的场景下,客户端发起请求,连接一直保持,服务端有数据就可以返回数据给客户端

    2024年02月21日
    浏览(33)
  • Springboot集成SSE实现消息推送之单工通信

    通常在一些web项目中,会涉及到想客户端推送消息,常见的有Ajax轮询、webSocket,本篇文章主要使用Springboot集成SSE实现向客户端持续推送信息。 服务发送事件SSE(Sever-Sent Event),就是基于 HTTP 的技术,浏览器向服务器发送一个保持长连接HTTP请求,服务器单向地向客户端以流形

    2024年01月17日
    浏览(36)
  • 使用post请求建立长连接实现sse,接收后端主动发来的消息,实现chat-gpt的弹字效果,EventSource的应用

    每日鸡汤:每个你想要学习的瞬间都是未来的你向自己求救 最近在做一个chat相关的功能,然后由于接口返回特别特别慢,所以需要搞一个慢慢等待的效果,就是接口一个单词一个单词的返回,然后前端收到一个展示一个,提升用户体验。 说实话我是第一次做这类需求,他们

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

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

    2024年02月08日
    浏览(44)
  • javaWEB消息推送之 WebSocket和SseEmitter

    用途 实时获取服务端的最新数据 查看调度任务的进度和执行状态 用户感知:修改数据后,相关用户收到信息 提升用户体验:耗时业务异步处理(Excel导入导出,复杂计算) 前端轮询 这种方式实现简单,前端通过 setInterval 定时去请求接口来获取最新的数据,当实时性要求不

    2024年01月24日
    浏览(26)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包