SSE(Server-Sent Events),SSE是一种基于HTTP协议的服务器向客户端推送数据的技术。它的优点是实现简单、轻量级,对现有服务器软件兼容性好。但是,由于SSE是单向通信模型,只能由服务器向客户端推送数据,对于需要客户端向服务器发送数据的场景,SSE就无法满足需求。
一, 主要特点
简单易用:SSE使用基于文本的数据格式,如纯文本、JSON等,使得数据的发送和解析都相对简单。
单向通信:SSE支持服务器向客户端的单向通信,服务器可以主动推送数据给客户端。
实时性:SSE建立长时间的连接,使得服务器可以实时地将数据推送给客户端,而无需客户端频繁地发起请求。
二,和WebSocket对比
WebSocket搭建看这个:10行Java代码实现站内信,一看就会-CSDN博客
SSE(Server-Sent Events)和WebSocket都是用于实现实时通信的技术,存在关键差异。
通信模型:SSE是单向通信模型,只能由服务器向客户端推送数据。而WebSocket是双向通信模型,客户端和服务器可以互相发送消息。
连接性:SSE使用长轮询或HTTP流技术,需要频繁地发起HTTP请求来获取数据。而 WebSocket只需在握手阶段建立一次连接,然后保持连接打开,减少了频繁建立连接的开销。
实时性:WebSocket提供了更低的延迟和更高的实时性,因为它支持双向通信,可以立即将数据推送给客户端。SSE虽然也可以实现实时性,但由于其单向通信模型,需要服务器定期发送数据。
协议特性:SSE是部署在HTTP协议之上的,现有的服务器软件都支持。而WebSocket是一个新的协议,需要服务器端支持对应的协议栈。
复杂性:SSE相对WebSocket来说更轻量级,实现更简单。WebSocket协议较复杂,实现相对困难一些。
总体来说,SSE和WebSocket都有各自的优点和适用场景。SSE轻量级且对现有服务器软件兼容性好,而WebSocket则提供了更强的双向通信能力和更高的实时性。
三,SSE实现
1,SpringBoot项目
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2,引入测试用的html文件的thymeleaf配置
spring:
mvc:
static-path-pattern: /**
web:
resources:
#静态文件目录index.html
static-locations: classpath:/templates/
3,接口实现
import cn.hutool.core.lang.UUID; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @RestController @RequestMapping("/sse") @Slf4j public class SseController { // 管理所有客户端的连接 private final Map<String, SseEmitter> sse = new ConcurrentHashMap<>() ; // 创建连接接口,指定消息类型为text/event-stream @GetMapping(path="/events/{id}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter createConnect(@PathVariable("id") String id) throws IOException { SseEmitter emitter = new SseEmitter(0L); // 将客户端保存到Map中 sse.put(id, emitter) ; // 当发生错误的回调 emitter.onError(ex -> { log.info("userId: {}, errot: {}", id, ex.getMessage()); sse.remove(id) ; }) ; // 异步请求完成后的回调 emitter.onCompletion(() -> { sse.remove(id) ; log.info("请求完成: {}", id); }) ; // 异步请求超时回调 emitter.onTimeout(() -> { sse.remove(id) ; log.info("请求超时: {}", id); }) ; return emitter; } // 由客户端发起请求,然后根据id获取相应的SseEmitter进行消息的发送 @GetMapping("/sender/{id}") public String sender(@PathVariable("id") String id) throws Exception { SseEmitter emitter = this.sse.get(id) ; if (emitter != null) { try { emitter.send( "随机消息 - " + UUID.fastUUID()) ; } catch (Exception e) { log.error("出现异常: ", e); } } return "success" ; } }
4,html测试文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SSE</title> </head> <body> <button type="button" οnclick="closeSse()">关闭</button> <hr style="margin: 2px; padding: 0px 0px;"/> <ul id="list"></ul> </body> <script> const evtSource = new EventSource(`/sse/events/${Date.now()}`) ; evtSource.onmessage = (event) => { const newElement = document.createElement("li") ; const eventList = document.getElementById("list") ; newElement.innerHTML = "接收到消息: " + event.data ; eventList.appendChild(newElement) ; }; evtSource.onopen = (event) => { console.log('建立连接...') }; evtSource.onerror = (event) => { console.error("发生错误:", event) ; }; function closeSse() { evtSource.close() ; } </script> </html>
5,测试
http://localhost:8201/sse/sender/321312321 模拟服务器端发送消息的接口
http://localhost:8201/sse/events/321312321 监听服务端消息,并展示出来
http://localhost:8201/sse.html 展示关闭按钮的页面
四,总结
服务器端和客户端(浏览器)的通信一直是比较常见的需求,SSE是比较适合快速开发的单向通信技术,如果遇到只需要服务器推送消息到客户端的,可以选择这个来实现。