如何在Spring Web的SSE中发送Ping心跳事件?
解决GraphQL SSE心跳事件发送问题(Spring环境)
核心问题分析
GraphQL SSE规范要求的心跳是发送event: :\n\n这种特殊格式的空事件,但Spring提供的ServerSentEvent对象强制要求构建完整的事件结构(比如指定event或data),无法直接生成这种特殊格式。下面提供两种可行的实现方案:
方案1:直接操作响应输出流(WebFlux环境)
绕过ServerSentEvent封装,直接向响应流写入原始的SSE格式内容,完全自定义输出:
@GetMapping(value = "/graphql/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public void handleGraphqlSse(ServerHttpResponse response) { response.getHeaders().setContentType(MediaType.TEXT_EVENT_STREAM); DataBufferFactory bufferFactory = response.bufferFactory(); // 每30秒发送一次心跳 Flux.interval(Duration.ofSeconds(30)) .doOnNext(tick -> { byte[] heartbeatBytes = "event: :\n\n".getBytes(StandardCharsets.UTF_8); DataBuffer buffer = bufferFactory.wrap(heartbeatBytes); // 写入响应流 response.writeWith(Mono.just(buffer)).subscribe(); }) // 连接断开时清理资源 .doFinally(signalType -> response.setComplete()) .subscribe(); // 此处可添加业务数据的发送逻辑,同样通过response.writeWith写入对应的SSE格式内容 }
方案2:返回原始字符串流(WebFlux/Servlet通用)
直接返回Flux<String>(WebFlux)或使用异步Servlet输出字符串,让Spring直接输出原始的SSE帧,无需经过ServerSentEvent的封装:
@GetMapping(value = "/graphql/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> graphqlSseStream() { // 业务数据事件流,按GraphQL SSE格式构造字符串 Flux<String> dataEvents = getGraphqlDataEvents(); // 心跳事件流 Flux<String> heartbeatEvents = Flux.interval(Duration.ofSeconds(30)) .map(tick -> "event: :\n\n"); // 合并业务流和心跳流,保持持续输出 return Flux.merge(dataEvents, heartbeatEvents) .onBackpressureBuffer(); } // 示例:构造业务数据的SSE事件 private Flux<String> getGraphqlDataEvents() { return Flux.just( "event: next\nid: 1\ndata: {\"data\":{...}}\n\n", "event: complete\nid: 2\n\n" ).delayElements(Duration.ofSeconds(10)); }
关于Spring的连接状态检测
Spring本身没有内置专门针对SSE的ping机制,上述自定义心跳的方式就是最直接的连接状态检测方案:客户端如果在指定间隔内未收到心跳,即可判定连接断开;服务端也可以通过响应流的订阅状态变化感知连接关闭。
内容的提问来源于stack exchange,提问作者sunalive
相关产品推荐
相关产品推荐

