ChatGPT流式显示单词的技术实现

問題:怎麼做出這種效果在这里插入图片描述

1. 前端

OpenAI 的 ChatGPT API 使用的是基于 HTTP/2 的协议来支持流式传输功能。HTTP/2 允许在单个连接上多路复用多个数据流,这使得流式传输成为可能。具体来说,OpenAI 的 API 可以通过 HTTP/2 的长连接进行流式数据传输,从而实现逐步接收和处理数据,而不是等待整个响应完成。

流式传输的工作原理

当你启用 stream=True 选项时,OpenAI 的 API 会逐步返回生成的内容。这种方式允许客户端在数据生成时立即处理,而不需要等待整个响应完成。这在处理大规模数据(如多个段落的文本或长对话)时特别有用,因为它可以显著减少延迟并提高用户体验。

流式传输的实现细节

  1. 客户端请求:客户端发送一个带有 stream=True 参数的请求。
  2. 服务器响应:服务器通过一个持续打开的 HTTP/2 连接逐步发送数据块(chunk)。
  3. 客户端处理:客户端逐块接收数据并处理,可以实时显示或进一步处理这些数据。

示例代码

以下是一个使用 Python 和 OpenAI API 的流式传输示例:

import openai  
   
# 初始化 OpenAI 客户端  
openai.api_key = 'your-api-key'  
   
# 发送请求并启用流式传输  
response = openai.Completion.create(  
    model="gpt-3.5-turbo",  
    prompt="Tell me a story.",  
    max_tokens=100,  
    stream=True  
)  
   
# 逐块处理响应  
for chunk in response:  
    if chunk.choices[0].delta.content is not None:  
        print(chunk.choices[0].delta.content, end='', flush=True)  

优点

  • 实时性:客户端可以立即处理和显示部分响应,而无需等待整个响应完成。
  • 高效性:减少了内存占用和延迟,提高了数据处理的效率。
  • 用户体验:用户可以更快地看到结果,尤其是在处理大规模数据时。

结论

OpenAI 的 ChatGPT API 使用 HTTP/2 协议来支持流式传输功能,使得客户端可以逐步接收和处理数据。这种方式不仅提高了实时性和效率,还显著改善了用户体验。

2. 后端如何实现HTTP/2的stream=True

在 Spring Boot 中处理 HTTP/2 和流式传输,可以利用 Spring WebFlux,它是一个用于构建反应式应用程序的框架。通过使用 Flux 对象,你可以逐步发送数据给前端,从而实现流式传输的效果。

下面是一个简单的示例,展示如何设置一个 Spring Boot 服务端来处理 HTTP/2 和流式传输,并逐步返回内容给前端。

1. 创建 Spring Boot 项目

你可以使用 Spring Initializr 创建一个新的 Spring Boot 项目,选择 Reactive Web 依赖项。

2. 配置 HTTP/2

确保你的 Spring Boot 项目配置了 HTTP/2。在 application.properties 中添加以下配置:

server.http2.enabled=true  

3. 创建控制器

创建一个控制器,使用 Flux 对象来逐步返回数据。

示例控制器
package com.example.demo;  
   
import org.springframework.http.MediaType;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
import reactor.core.publisher.Flux;  
   
import java.time.Duration;  
import java.util.stream.Stream;  
   
@RestController  
public class StreamController {  
  
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)  
    public Flux<String> stream() {  
        Stream<String> stream = Stream.of(  
                "Hello, ",  
                "this ",  
                "is ",  
                "a ",  
                "stream ",  
                "of ",  
                "words."  
        );  
  
        return Flux.fromStream(stream)  
                .delayElements(Duration.ofSeconds(1));  
    }  
}  

4. 启动应用

确保你已经安装了支持 HTTP/2 的 JDK(例如 JDK 11 或更高版本),然后启动 Spring Boot 应用。

5. 前端代码

前端可以使用 Fetch API 来处理流式响应。以下是一个简单的示例,展示如何在前端逐步接收和显示数据。

前端 JavaScript 示例
<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>Stream Example</title>  
</head>  
<body>  
    <div id="output"></div>  
    <script>  
        const outputDiv = document.getElementById('output');  
  
        fetch('/stream')  
            .then(response => {  
                const reader = response.body.getReader();  
                const decoder = new TextDecoder();  
  
                function read() {  
                    reader.read().then(({ done, value }) => {  
                        if (done) {  
                            console.log('Stream complete');  
                            return;  
                        }  
  
                        const text = decoder.decode(value, { stream: true });  
                        outputDiv.innerHTML += text;  
  
                        read();  
                    });  
                }  
  
                read();  
            });  
    </script>  
</body>  
</html>  

解释

  1. Spring Boot 后端
    • 在控制器中使用 Flux<String> 返回一个字符串流。
    • @GetMapping 注解指定了路径 /stream 并设置了响应类型为 text/event-stream
    • 使用 Stream.of 创建一个字符串流,并通过 Flux.fromStream 转换为 Flux 对象。
    • delayElements(Duration.ofSeconds(1)) 用于模拟逐步返回数据,每个元素之间有 1 秒的延迟。

3. 扩展:Spring WebFlux是servlet嗎

Spring WebFlux 不是基于 Servlet 的框架。它是一个完全独立的反应式 Web 框架,设计用于处理非阻塞、事件驱动的请求。Spring WebFlux 使用的是 Reactive Streams API(如 Reactor)而不是 Servlet API。

主要区别

  1. 架构

    • Spring MVC:基于 Servlet API 和同步阻塞的编程模型。每个请求对应一个线程,由线程池管理。
    • Spring WebFlux:基于 Reactive Streams API 和非阻塞的编程模型,通常使用 Netty、Undertow 或者支持非阻塞 I/O 的 Servlet 3.1+ 容器(如 Tomcat 和 Jetty)。
  2. 线程模型

    • Spring MVC:每个请求分配一个线程,线程阻塞直到请求完成。这种模型容易受到线程饥饿的影响,特别是在处理大量长时间运行的请求时。
    • Spring WebFlux:使用少量固定的线程处理大量请求,通过事件驱动和非阻塞 I/O 实现高并发。

结论

Spring WebFlux 是一个独立于 Servlet 的反应式 Web 框架,设计用于处理高并发和非阻塞请求。它使用 Reactive Streams API,而不是传统的 Servlet API。这使得它在处理需要高并发和低延迟的应用程序时具有显著优势。

4. 扩展:Reactive Streams API是谁定义的,和Reactor有什么区别

Reactive Streams API 是由多个公司和组织共同定义的一个标准,用于处理异步流数据,提供背压机制。它是一个 SPI(服务提供者接口),定义了四个核心接口:PublisherSubscriberSubscriptionProcessor

Reactive Streams API

Reactive Streams API 由以下公司和组织共同定义:

  • Lightbend (原 Typesafe)
  • Netflix
  • Pivotal
  • Red Hat
  • Oracle

这些公司和组织共同努力,创建了一个用于异步流处理的标准。

核心接口
  1. Publisher:

    • void subscribe(Subscriber<? super T> s);
    • 生产者,负责发布数据。
  2. Subscriber:

    • void onSubscribe(Subscription s);
    • void onNext(T t);
    • void onError(Throwable t);
    • void onComplete();
    • 消费者,订阅并处理来自 Publisher 的数据。
  3. Subscription:

    • void request(long n);
    • void cancel();
    • PublisherSubscriber 使用,用于控制数据流和取消订阅。
  4. Processor:

    • 继承 PublisherSubscriber,可以在处理链中作为中间处理器。

Reactor

Reactor 是由 Pivotal(现属于 VMware)开发的一个反应式库,基于 Reactive Streams API 构建。它是 Spring WebFlux 的核心基础之一。Reactor 提供了两个主要的类来处理异步序列:

  1. Flux: 表示 0 到 N 个元素的流。
  2. Mono: 表示 0 或 1 个元素的流。

Reactor 提供了丰富的操作符和工具,用于处理异步流数据,包括映射、过滤、组合和处理错误等。

区别

  • 定义与实现

    • Reactive Streams API:是一个标准和规范,定义了接口和交互模式,但不提供具体实现。
    • Reactor:是 Reactive Streams API 的具体实现之一,提供了丰富的操作符和工具用于处理异步流数据。
  • 使用场景

    • Reactive Streams API:用于定义和实现自己的反应式库或框架。
    • Reactor:用于构建基于反应式编程模型的应用,特别是与 Spring WebFlux 一起使用。

示例代码

以下是一个简单的 Reactor 示例,展示如何使用 FluxMono

import reactor.core.publisher.Flux;  
import reactor.core.publisher.Mono;  
   
public class ReactorExample {  
  
    public static void main(String[] args) {  
        // 创建一个 Flux,表示 0 到 N 个元素的流  
        Flux<String> flux = Flux.just("Hello", "World", "from", "Reactor");  
  
        // 订阅并打印每个元素  
        flux.subscribe(System.out::println);  
  
        // 创建一个 Mono,表示 0 或 1 个元素的流  
        Mono<String> mono = Mono.just("Single Value");  
  
        // 订阅并打印元素  
        mono.subscribe(System.out::println);  
    }  
}  

这个示例展示了如何使用 Reactor 创建和订阅流数据。FluxMono 是 Reactor 的核心类,提供了丰富的操作符用于处理异步流数据。

结论

Reactive Streams API 是一个标准,定义了处理异步流数据的接口和模式,而 Reactor 是基于这个标准实现的具体库,提供了丰富的操作符和工具,用于构建反应式应用程序。

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值