实时数据推送并非只有WebSocket一种选择

环境:SpringBoot2.7.16
概述在Web应用中,有几种实时数据推送的选择方案,包括SSE(Server-Sent Events)、WebSocket、长轮询等 。
SSE是一种基于HTTP协议的服务器向客户端推送数据的技术 。它的优点是实现简单、轻量级,对现有服务器软件兼容性好 。但是,由于SSE是单向通信模型,只能由服务器向客户端推送数据,对于需要客户端向服务器发送数据的场景,SSE就无法满足需求 。
WebSocket是一种双向通信模型,允许客户端和服务器之间互相发送消息 。它的优点是实时性强、延迟低 , 但是需要服务器端支持对应的协议栈 , 实现起来相对复杂一些 。
长轮询是对短轮询的一种改进版本,通过在尽可能减少对服务器资源浪费的同时,保证消息的相对实时性 。长轮询在客户端发起请求时,服务器会保持连接打开,等待一定时间后再返回响应 。这样可以减少客户端频繁的请求,节省带宽和服务器资源 。但是,如果服务器没有新的消息产生 , 客户端会一直等待响应,实时性就会受到一定影响 。
根据实际应用场景和需求,可以选择适合的实时数据推送方案 。如果只需要服务器向客户端推送数据,且对实时性要求不是特别高,可以选择SSE 。如果需要客户端向服务器发送数据,或者对实时性要求较高,可以选择WebSocket或长轮询 。当然,也可以根据实际情况将这几种方案结合起来使用,以满足不同的需求 。
SSE与WebSocket对比SSE(Server-Sent Events)和WebSocket都是用于实现实时通信的技术 , 存在关键差异 。
【实时数据推送并非只有WebSocket一种选择】通信模型:SSE是单向通信模型,只能由服务器向客户端推送数据 。而WebSocket是双向通信模型,客户端和服务器可以互相发送消息 。
连接性:SSE使用长轮询或HTTP流技术,需要频繁地发起HTTP请求来获取数据 。而 WebSocket只需在握手阶段建立一次连接,然后保持连接打开,减少了频繁建立连接的开销 。
实时性:WebSocket提供了更低的延迟和更高的实时性,因为它支持双向通信,可以立即将数据推送给客户端 。SSE虽然也可以实现实时性,但由于其单向通信模型,需要服务器定期发送数据 。
协议特性:SSE是部署在HTTP协议之上的,现有的服务器软件都支持 。而WebSocket是一个新的协议,需要服务器端支持对应的协议栈 。
复杂性:SSE相对WebSocket来说更轻量级 , 实现更简单 。WebSocket协议较复杂,实现相对困难一些 。
总体来说,SSE和WebSocket都有各自的优点和适用场景 。SSE轻量级且对现有服务器软件兼容性好,而WebSocket则提供了更强的双向通信能力和更高的实时性 。
SSE简介SSE(Server-Sent Events)是一种用于实现服务器向客户端实时推送数据的Web技术 。与传统的轮询和长轮询相比,SSE提供了更高效和实时的数据推送机制 。
SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端 。客户端通过建立持久的HTTP连接,并监听事件流 , 可以实时接收服务器推送的数据 。
SSE的主要特点包括:
简单易用:SSE使用基于文本的数据格式,如纯文本、JSON等,使得数据的发送和解析都相对简单 。
单向通信:SSE支持服务器向客户端的单向通信 , 服务器可以主动推送数据给客户端 。
实时性:SSE建立长时间的连接,使得服务器可以实时地将数据推送给客户端 , 而无需客户端频繁地发起请求 。
服务端开发依赖管理
<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>配置文件
spring:mvc:static-path-pattern: /**web:resources:#静态文件目录index.htmlstatic-locations: classpath:/templates/接口开发
@RestController@RequestMApping("/sse")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 -> {System.err.printf("userId: %s, error: %s%n", id, ex.getMessage()) ;sse.remove(id) ;}) ;// 异步请求完成后的回调emitter.onCompletion(() -> {sse.remove(id) ;System.out.printf("%s, 请求完成...") ;}) ;// 异步请求超时回调emitter.onTimeout(() -> {System.err.println("超时...") ;}) ;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( "随机消息 - " + new Random().nextInt(10000000)) ;} catch (Exception e) {System.err.println("%s%n", e.getMessage()) ;}}return "success" ;}}


推荐阅读