前端实现与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则更为轻量级。无论选择哪种方案,掌握长连接和流式数据处理都是构建现代化实时交互应用的关键。

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

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

相关推荐
稚南城才子,乌衣巷风流1 小时前
函数:编程中的核心概念
开发语言·前端·javascript
IT_陈寒2 小时前
为什么我的JavaScript异步代码总是不按顺序执行?
前端·人工智能·后端
索西引擎2 小时前
【React】useState 状态更新机制:批量更新策略与“异步“错觉的深层解析
前端·react.js·前端框架
慢功夫2 小时前
💡第五篇:VSCode插件是如何与主进程通信的?
前端·visual studio code
锋行天下2 小时前
打造企业内部知识库系统RAG全栈项目
前端·后端·架构
湫夨兮3 小时前
如何配置codex cli和codex app(官方已经合并,现在名字为chatgpt)
chatgpt·codex
竹林8183 小时前
用 ethers.js 连接 MetaMask 实现钱包登录:一个真实项目中的完整踩坑记录
前端·javascript
虚惊一场3 小时前
在浏览器里跑 Prettier:格式化 Markdown 的四个难点
前端·javascript·vue.js
前端炒粉3 小时前
手撕小汇总
java·前端·javascript
r_oo_ki_e_3 小时前
vue快速入门
前端·vue.js