暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Web实时消息推送的7种实现方案(二)

zhangyfr 2025-07-02
180

iframe流

iframe流就是在页面中插入一个隐藏的<iframe>标签,通过在src中请求消息数量API接口,由此在服务端和客户端之间创建一条长连接,服务端持续向iframe传输数据。

传输的数据通常是HTML、或是内嵌的javascript脚本,来达到实时更新页面的效果。

image.png
这种方式实现简单,前端只要一个<iframe>标签搞定了

<iframe src="/iframe/message" style="display:none"></iframe>

服务端直接组装html、js脚本数据向response写入就行了

@Controller @RequestMapping("/iframe") publicclass IframeController {     @GetMapping(path = "message")     public void message(HttpServletResponse response) throws IOException, InterruptedException {         while (true) {             response.setHeader("Pragma""no-cache");             response.setDateHeader("Expires"0);             response.setHeader("Cache-Control""no-cache,no-store");             response.setStatus(HttpServletResponse.SC_OK);             response.getWriter().print(" <script type=\"text/javascript\">\n" +                     "parent.document.getElementById('clock').innerHTML = \"" + count.get() + "\";" +                     "parent.document.getElementById('count').innerHTML = \"" + count.get() + "\";" +                     "</script>");         }     } }

但我个人不推荐,因为它在浏览器上会显示请求未加载完,图标会不停旋转,简直是强迫症杀手。

SSE

很多人可能不知道,服务端向客户端推送消息,其实除了可以用WebSocket这种耳熟能详的机制外,还有一种服务器发送事件(Server-sent events),简称SSE

SSE它是基于HTTP协议的,我们知道一般意义上的HTTP协议是无法做到服务端主动向客户端推送消息的,但SSE是个例外,它变换了一种思路。
WechatIMG15.png
SSE在服务器和客户端之间打开一个单向通道,服务端响应的不再是一次性的数据包而是text/event-stream类型的数据流信息,在有数据变更时从服务器流式传输到客户端。

整体的实现思路有点类似于在线视频播放,视频流会连续不断的推送到浏览器,你也可以理解成,客户端在完成一次用时很长(网络不畅)的下载。
image.png
SSEWebSocket作用相似,都可以建立服务端与浏览器之间的通信,实现服务端向客户端推送消息,但还是有些许不同:

  • SSE 是基于HTTP协议的,它们不需要特殊的协议或服务器实现即可工作;WebSocket需单独服务器来处理协议。
  • SSE 单向通信,只能由服务端向客户端单向通信;webSocket全双工通信,即通信的双方可以同时发送和接受信息。
  • SSE 实现简单开发成本低,无需引入其他组件;WebSocket传输数据需做二次解析,开发门槛高一些。
  • SSE 默认支持断线重连;WebSocket则需要自己实现。
  • SSE 只能传送文本消息,二进制数据需要经过编码后传送;WebSocket默认支持传送二进制数据。

SSE 与 WebSocket 该如何选择?

技术并没有好坏之分,只有哪个更合适

SSE好像一直不被大家所熟知,一部分原因是出现了WebSockets,这个提供了更丰富的协议来执行双向、全双工通信。对于游戏、即时通信以及需要双向近乎实时更新的场景,拥有双向通道更具吸引力。

但是,在某些情况下,不需要从客户端发送数据。而你只需要一些服务器操作的更新。比如:站内信、未读消息数、状态更新、股票行情、监控数量等场景,SEE不管是从实现的难易和成本上都更加有优势。此外,SSE 具有WebSockets在设计上缺乏的多种功能,例如:自动重新连接、事件ID和发送任意事件的能力。

前端只需进行一次HTTP请求,带上唯一ID,打开事件流,监听服务端推送的事件就可以了

<script>     let source = null;     let userId = 7777     if (window.EventSource) {         // 建立连接         source = new EventSource('http://localhost:7777/sse/sub/'+userId);         setMessageInnerHTML("连接用户=" + userId);         /**          * 连接一旦建立,就会触发open事件          * 另一种写法:source.onopen = function (event) {}          */         source.addEventListener('open', function (e) {             setMessageInnerHTML("建立连接。。。");         }, false);         /**          * 客户端收到服务器发来的数据          * 另一种写法:source.onmessage = function (event) {}          */         source.addEventListener('message', function (e) {             setMessageInnerHTML(e.data);         });     } else {         setMessageInnerHTML("你的浏览器不支持SSE");     } </script>

服务端的实现更简单,创建一个SseEmitter对象放入sseEmitterMap进行管理

private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>(); /**  * 创建连接  *  * @date: 2022/7/12 14:51  */ public static SseEmitter connect(String userId) {     try {         // 设置超时时间,0表示不过期。默认30秒         SseEmitter sseEmitter = new SseEmitter(0L);         // 注册回调         sseEmitter.onCompletion(completionCallBack(userId));         sseEmitter.onError(errorCallBack(userId));         sseEmitter.onTimeout(timeoutCallBack(userId));         sseEmitterMap.put(userId, sseEmitter);         count.getAndIncrement();         return sseEmitter;     } catch (Exception e) {         log.info("创建新的sse连接异常,当前用户:{}", userId);     }     returnnull; } /**  * 给指定用户发送消息  *  * @date: 2022/7/12 14:51  */ public static void sendMessage(String userId, String message) {     if (sseEmitterMap.containsKey(userId)) {         try {             sseEmitterMap.get(userId).send(message);         } catch (IOException e) {             log.error("用户[{}]推送异常:{}", userId, e.getMessage());             removeUser(userId);         }     } }

我们模拟服务端推送消息,看下客户端收到了消息,和我们预期的效果一致。
注意: SSE不支持IE浏览器,对其他主流浏览器兼容性做的还不错。
640.png

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论