JAVA后端调用OpenAI接口 实现打字机效果(SSE)
SSE
SSE(Server-Sent Events,服务器发送事件)是一种基于HTTP协议的通信技术,它允许服务器持续地将数据推送给客户端,而无需客户端发起请求。这种通信方式通常用于实时性要求较高的场景,如实时更新、通知、或者数据流式传输。
SSE与传统的Ajax轮询或长轮询相比,具有更低的延迟、更高的效率,并且更易于实现。它建立在HTTP协议之上,利用HTTP/1.1的持久连接,允许服务器在连接建立后持续地向客户端发送数据,客户端通过监听一个HTTP连接来接收这些数据。
在Web开发中,服务器通常会使用特殊的HTTP响应头(如"Content-Type: text/event-stream")来指示客户端这是一个SSE流,并且按照一定的格式发送事件数据给客户端。客户端则可以使用JavaScript中的EventSource对象来接收并处理这些事件,从而实现实时的数据更新。
SseEmitter
SseEmitter是Spring框架中的一个类,专门用于Java。SSE代表服务器发送事件,是一种使服务器能够通过HTTP向Web客户端推送数据更新的技术。SseEmitter是在Spring应用程序中实现SSE服务器支持的便捷方式。
使用SseEmitter,您可以在Spring应用程序中创建一个端点,客户端可以连接到该端点,服务器可以通过此连接向客户端推送事件。这对于实时更新非常有用,例如显示实时通知、进度更新或流式传输数据。
实现:
- OpenAI支持Stream流格式接收

接口连续的数据读取
官网示例

{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo-0125", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo-0125", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
...
{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo-0125", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
demo
private static final String API_KEY = "********************";private static final Pattern contentPattern = Pattern.compile("\"content\":\"(.*?)\"}");private static final String MODEL_ENGINE = "gpt-3.5-turbo";public static void test() throws InterruptedException, IOException {//params 的入参封装 这里省略 参考上面图片 或去官网 需要stream形式请求HttpRequest httpRequest = HttpRequest.post("https://api.openai.com/v1/chat/completions").header("Content-Type", "application/json").header("Authorization", "Bearer " + API_KEY).body(JSONUtil.toJsonStr(params));//TODO 代理 到shadowsocks httpRequest.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890)));HttpResponse execute = httpRequest.execute();InputStream inputStream = execute.bodyStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));String line;while ((line = bufferedReader.readLine()) != null) {if (StringUtils.hasLength(line)) {System.out.println(line);Matcher matcher = contentPattern.matcher(line);if (matcher.find()) {String content = matcher.group(1);System.out.println(content);}}}
}
SSE发送
demo
ChatController
@Autowiredprivate ChatService chatService;@GetMapping("/test")public SseEmitter test(String question) {SseEmitter sseEmitter = new SseEmitter();chatService.question(question, sseEmitter);return sseEmitter;}
ChatService
private static final String API_KEY = "********************";private static final Pattern contentPattern = Pattern.compile("\"content\":\"(.*?)\"}");@Asyncpublic void question(String question, SseEmitter sseEmitter) {try {// 构建请求参数String params = "{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"user\",\"content\":\"" + question + "\"}],\"stream\":true}";// 发起 HTTP 请求HttpRequest httpRequest = HttpRequest.post("https://api.openai.com/v1/chat/completions").header("Content-Type", "application/json").header("Authorization", "Bearer " + API_KEY).body(JSONUtil.toJsonStr(params)).setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890)));// 执行 HTTP 请求HttpResponse execute = httpRequest.execute();// 处理响应流try (InputStream inputStream = execute.bodyStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {String line;while ((line = bufferedReader.readLine()) != null) {if (StringUtils.hasLength(line)) {// 输出响应内容System.out.println(line);// 提取内容Matcher matcher = contentPattern.matcher(line);if (matcher.find()) {String content = matcher.group(1);System.out.println(content);// 发送 SSE 事件 (模拟延迟)Thread.sleep(1000);sseEmitter.send(SseEmitter.event().name("answer").data("{" + content + "}"));}}}}} catch (IOException | InterruptedException e) {// 异常处理throw new RuntimeException(e);} finally {// 完成 SSE 连接sseEmitter.complete();}}
测试

相关文章:
JAVA后端调用OpenAI接口 实现打字机效果(SSE)
SSE SSE(Server-Sent Events,服务器发送事件)是一种基于HTTP协议的通信技术,它允许服务器持续地将数据推送给客户端,而无需客户端发起请求。这种通信方式通常用于实时性要求较高的场景,如实时更新、通知、或…...
超店建站携手太洋物产,共建跨境生意增长解决方案
2024年3月21日,至真科技旗下的超店建站与太洋物产在出海业务上达成了合作意向,标志着双方共同构建海外版图的合作正式启动。此次合作充分彰显了超店建站在海外业务方面的卓越技术能力和丰富经验,赢得了太洋物产的高度认可。 当天,…...
提高企业员工生产力的办法
在现代商业环境中,提高企业员工生产力是企业持续发展的关键因素之一。员工生产力的提升不仅有助于企业提高运营效率,还能增强企业的市场竞争力。那么,如何有效地提高企业员工生产力呢?本文将就此问题进行探讨。 一、引入先进技术软…...
XML Data – Semi-Structured Data XML 数据 - 半结构化数据
Outline • Structured, Semistructured, and Unstructured Data • XML Hierarchical (Tree) Data Model • Extracting XML Documents from Relational Databases • XML Documents, DTD, and XML Schema • XML Languages 结构化、半结构化和非结构化数据 - XML 层次&#x…...
Python自动化之如何利用allure生成测试报告
Allure测试报告框架帮助你轻松实现”高大上”报告展示。本文通过示例演示如何从0到1集成Allure测试框架。重点展示了如何将Allure集成到已有的自动化测试工程中、以及如何实现报表的优化展示。Allure非常强大,支持多种语言多种测试框架,无论是Java/Pytho…...
【晴问算法】入门篇—贪心算法—区间不相交问题
题目描述 给定n个开区间,从中选择尽可能多的开区间,使得这些开区间两两没有交集。 输入描述 输出描述 输出一个整数,表示最多选择的开区间个数。 样例1输入 4 1 3 2 4 3 5 6 7 输出 3 解释 最多选择(1,3)、(3,5)、(6,7)三个区间,它…...
WPF意外无法启动?try-catch也无法捕捉?0xc0000409?
文章目录 背景尝试原因解决 背景 周六在家加了一会会的班,公司电脑没关机,然后周一上班。。。诡异的事情发生了,在家远程都能运行的程序,突然运行不起来了 尝试 我对WPF程序做了如下尝试: 修改UI框架对OnStartup方…...
微服务day05(中) -- ES索引库操作
索引库就类似数据库表,mapping映射就类似表的结构。 我们要向es中存储数据,必须先创建“库”和“表”。 2.1.mapping映射属性 mapping是对索引库中文档的约束,常见的mapping属性包括: type:字段数据类型,…...
AI智能电销机器人可以做哪些事情呢?智能机器人搭建
随着科技不断发展,选择使用电销智能机器人的行业有很多,因为它的适用性和实用性都非常广,电销智能机器人使用之后能够让企业的整体工作效率变得更加高效,全天候灵活响应也能帮助企业做好业务工作。对于一些还不太了解电销智能机器…...
别踩坑!2024年小红书代写代发机构选择指南!
在小红书平台上,一篇优质的内容往往能迅速吸引用户的关注,为品牌带来不可估量的曝光和转化。然而,对于许多品牌来说,创作高质量的小红书内容并非易事。因此,选择一家专业的小红书代写代发机构成为了不少品牌的明智之选…...
数据出路 -----pandas
Pandas 是 Python 语言的一个扩展程序库,用于数据分析。 Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据)。 数据结构 Series 是一种类似于一维数组的对象,它由一组数据(各…...
Win11右键菜单定制
0.优化目标 优化成:右键菜单优化成全量菜单选项,并精简掉我不需要的菜单选项。 具体优化步骤: 1.win11菜单恢复到win10经典状态 win11右键菜单是缩水版的,需要再次点击“显示更多选项”才能找到自己想用到的选项,再…...
将深度图转成2D激光
文章目录 大致过程具体实现运行结果大致过程 将深度图转换为2D激光扫描数据是一个在机器人和自动化领域常见的任务,尤其是在计算资源有限的情况下或当只需要2D数据时。这个过程通常涉及从深度图中选择一个水平切片(或多个切片)并将其转换为距离读数。以下是基本步骤: 确定…...
rust学习笔记(8-12)
8 集合 Rust 标准库中包含一系列被称为 集合(collections)的非常有用的数据结构。大部分其他数 据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些 集合指向的数据是储存在堆上的,这…...
JetPack之DataBinding基础使用
目录 一、简介二、使用2.1 使用环境2.2 xml文件绑定数据2.3 数据绑定的对象2.3.1 object2.3.2 ObseravbleField2.3.3 ObseravbleCollection 2.4 绑定数据 三、应用场景 一、简介 DataBinding是谷歌15年推出的library,DataBinding支持双向绑定,能大大减少绑定app逻辑…...
设计模式学习笔记 - 设计原则与思想总结:2.运用学过的设计原则和思想完善之前性能计数器项目
概述 在 《设计原则 - 10.实战:针对非业务的通用框架开发,如何做需求分析和设计及如何实现一个支持各种统计规则的性能计数器》中,我们讲解了如何对一个性能计数器框架进行分析、设计与实现,并且实践了一些设计原则和设计思想。当…...
docker入门(八)—— dockerfile详细介绍,编写dockerfile
dockerfile(重点) 大家想想,Nginx,tomcat,mysql 这些镜像都是哪里来的?官方能写,我们不能写吗? 我们要研究自己如何做一个镜像,而且我们写的微服务项目打包上云部署&am…...
机器学习复习(9)——自定义dataset
目录 第一种dataset(文件夹名即为标签) 用于将格式(1)转换为格式(2) 第二种dataset(标签在labels文件夹下的对应的txt文件里面) 第一种dataset(文件夹名即为标签) 数据组织格式(1) --data ----train …...
【Redis】缓存穿透
问题发生背景:客户端请求的数据再缓存中和数据库中都不存在。 导致的问题:缓存永远不会生效,这些请求都会去请求数据库—导致数据库压力增大。 解决方案: 1.缓存空对象 在Redis中缓存空对象,告诉客户端数据库中没有该值…...
编程出现bug?怎么用Python打印异常
在 Python 编程中,异常是指程序执行过程中出现的错误或异常情况。当程序遇到异常时,为了更好地调试和定位问题,我们需要打印异常信息。本文将详细介绍如何在 Python 中打印异常,并提供一些示例和注意事项。 一、try-except 语句捕…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
