前端实现与Spring后端请求的实时流式响应,打造类 AI流式回复效果

在当今实时交互体验至上的时代,像ChatGPT那样流畅的流式回复体验越来越受欢迎。本文将深入浅出地讲解如何使用前端技术与Spring后端强强联手,打造令人惊艳的流式回复功能。

一、基本概念

在正式开始之前,我们先来了解一些基础概念:

  • 长连接: 区别于传统的HTTP请求-响应模式,长连接允许服务器主动向客户端推送数据,而无需等待客户端发起请求。
  • 流式传输: 数据并非一次性传输完毕,而是像流水一样持续地传输,让接收方可以实时处理。
  • WebSocket: HTML5提供的全双工通信协议,能够实现浏览器与服务器之间的双向实时通信。
  • SSE (Server-Sent Events): 服务端推送技术,浏览器通过HTTP连接接收服务器单向推送的数据流。
  • Spring WebFlux: Spring生态系统中用于构建反应式应用程序的框架,支持WebSocket和SSE,能够处理流式数据。

二、为什么需要实时流式响应

传统的HTTP请求-响应模式下,客户端必须不断地发送请求才能获取最新的数据,这种方式效率低下且资源消耗大。而长连接允许服务器主动推送数据,实时性更高,也更加节省资源。

三、技术选型与原理

1. 前端:

  • WebSocket: HTML5提供的全双工通信协议,完美契合长连接和流式数据传输的需求。
  • SSE (Server-Sent Events): 服务端推送技术,浏览器通过HTTP连接接收服务器单向推送的数据流。

2. 后端 (Spring):

  • Spring WebFlux: Spring生态系统中用于构建反应式应用程序的框架,支持WebSocket和SSE。

3. 原理:

  • 建立连接: 前端通过WebSocket或SSE与后端建立持久连接。
  • 发送请求: 前端发送消息请求到后端。
  • 流式处理: 后端接收请求后,不等待完整结果,而是将处理过程中的中间结果(例如逐字生成的回复)实时推送给前端。
  • 动态展示: 前端接收数据流,并动态更新页面内容,实现逐字显示的效果。

四、WebSocket方案实现

4.1 技术原理
  • 建立连接:
    • 前端使用 new WebSocket() 创建WebSocket对象,连接到后端指定的WebSocket地址。
    • 后端使用Spring WebFlux提供的 WebSocketHandler 接口处理WebSocket连接请求。
  • 数据传输:
    • 前端通过 WebSocket.send() 发送消息到后端。
    • 后端通过 WebSocketSession.send() 方法向客户端推送消息。
    • 前后端都可以监听 onmessage 事件来接收对方发送的消息。
  • 关闭连接:
    • 前端或后端都可以调用 WebSocket.close() 方法关闭连接。
4.2. 后端 (Spring WebFlux):
复制代码
@Component
public class ChatWebSocketHandler implements WebSocketHandler {

    @Override
    public Mono<Void> handle(WebSocketSession session) {
        // 接收消息
        return session.receive()
                .map(WebSocketMessage::getPayloadAsText)
                .flatMap(message -> {
                    // 处理消息,例如调用ChatGPT API
                    return processMessage(message);
                })
                .flatMap(reply -> {
                    // 将回复逐字发送
                    return Flux.fromIterable(reply.split(""))
                            .delayElements(Duration.ofMillis(50)) // 模拟延迟
                            .map(session::textMessage)
                            .map(session::send);
                })
                .then(); 
    }

    // 模拟调用ChatGPT API并逐字返回回复
    private Mono<String> processMessage(String message) {
        return Mono.just("收到消息:" + message + ",正在思考中...").delayElement(Duration.ofSeconds(1))
                .flatMap(initialReply -> {
                    return Flux.range(0, initialReply.length())
                            .map(i -> initialReply.substring(0, i + 1))
                            .delayElements(Duration.ofMillis(100)) // 模拟逐字生成
                            .reduce((a, b) -> b) // 只取最后一个结果
                            .cast(String.class);
                });
    }
}
4.3. 前端 (JavaScript):
复制代码
const socket = new WebSocket('ws://localhost:8080/chat');

socket.onopen = () => {
  console.log('WebSocket 连接已建立');
};

socket.onmessage = (event) => {
  const message = event.data;
  document.getElementById('chat-output').innerHTML += message; 
};

function sendMessage() {
  const message = document.getElementById('chat-input').value;
  socket.send(message);
}

五、SSE方案实现

4.1 技术原理
  • 建立连接: 前端使用 new EventSource() 创建EventSource对象,连接到后端指定的SSE接口。
  • 数据传输:
    • 后端通过 Flux<ServerSentEvent<T>> 返回数据流,Spring WebFlux会自动将数据流转换为SSE格式。
    • 前端监听 onmessage 事件接收后端推送的数据。
  • 关闭连接: 前端调用 EventSource.close() 关闭连接,或者后端停止发送数据。
4.2 代码示例

后端 (Spring WebFlux):

复制代码
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamEvents() {
    return Flux.interval(Duration.ofMillis(100))
            .map(sequence -> ServerSentEvent.<String>builder()
                    .data("数据流 - " + sequence)
                    .build());
}

前端 (JavaScript):

复制代码
const eventSource = new EventSource('/stream');

eventSource.onmessage = (event) => {
  const message = event.data;
  document.getElementById('output').innerHTML += message + '<br>';
};

六、方案比较

特点 WebSocket SSE
连接类型 全双工 单向(服务器到客户端)
数据传输 双向 单向
应用场景 需要双向实时通信,如聊天、游戏 服务器推送数据,如实时更新、通知
复杂度 较高 较低

七、总结

本文只是大概介绍了如何利用WebSocket和SSE两种技术实现前端与Spring后端的长连接,并以ChatGPT的流式回复为例,给出了具体的代码实现和技术原理分析。

在实际应用中,需要根据具体需求选择合适的方案。如果需要双向实时通信,WebSocket是更优选择;如果只需服务器单向推送数据,SSE则更为轻量级。无论选择哪种方案,掌握长连接和流式数据处理都是构建现代化实时交互应用的关键。

希望本文能帮助你更好地入门去理解和应用实时流式响应技术,为用户打造更加流畅、自然的交互体验!

后续还会有更加深入去理解和实现技术!

相关推荐
xixixin_13 小时前
【React】为什么移除事件要写在useEffect的return里面?
前端·javascript·react.js
嘗_13 小时前
react 源码2
前端·javascript·react.js
我只会写Bug啊17 小时前
Vue文件预览终极方案:PNG/EXCEL/PDF/DOCX/OFD等10+格式一键渲染,开源即用!
前端·vue.js·pdf·excel·预览
扯蛋43818 小时前
LangChain的学习之路( 一 )
前端·langchain·mcp
Mr.Jessy18 小时前
Web APIs学习第一天:获取 DOM 对象
开发语言·前端·javascript·学习·html
ConardLi20 小时前
Easy Dataset 已经突破 11.5K Star,这次又带来多项功能更新!
前端·javascript·后端
芒克芒克20 小时前
ssm框架之Spring(上)
java·后端·spring
冴羽20 小时前
10 个被严重低估的 JS 特性,直接少写 500 行代码
前端·javascript·性能优化
rising start20 小时前
四、CSS选择器(续)和三大特性
前端·css
晨晖220 小时前
SpringBoot的yaml配置文件,热部署
java·spring boot·spring