使用go实现流式输出

流式输出的深度剖析

之前一直在调用openai的key,只是照着文档进行流式调用,也只知其确是流式与api有所不同,而未成体系深究其实现原理。

就以openai的官方流式输出为切入。

概述

流式输出(Streaming Output)是 HTTP 响应中的一种模式,服务器可以在生成部分内容时立即将这些内容发送给客户端,而无需等待整个响应内容生成完成。这种方式常用于实时交互、高延迟操作或长时间任务中,比如 OpenAI 的 GPT 模型生成流式对话。

package mainimport ("bufio""bytes""encoding/json""fmt""net/http""strings""time"
)// 定义必要的数据结构
type Message struct {Role    string `json:"role"`Content string `json:"content"`
}type RequestBody struct {Model       string    `json:"model"`Messages    []Message `json:"messages"`Temperature float64   `json:"temperature"`Stream      bool      `json:"stream"`
}type Choice struct {Delta struct {Content string `json:"content"`} `json:"delta"`
}type ResponseBody struct {Choices []Choice `json:"choices"`
}const (apiURL      = "https://api.example.com/v1/chat/completions" // 替换为实际的 API 地址authToken   = "your-auth-token"                             // 替换为实际的 Tokenmodel       = "gpt-3.5-turbo"temperature = 0.7
)func StreamHandler(w http.ResponseWriter, r *http.Request) {// 从查询参数获取输入内容content := r.URL.Query().Get("content")if content == "" {http.Error(w, "Missing 'content' parameter", http.StatusBadRequest)return}// 构造请求体message := Message{Role:    "user",Content: content,}requestBody := RequestBody{Model:       model,Messages:    []Message{message},Temperature: temperature,Stream:      true,}jsonData, err := json.Marshal(requestBody)if err != nil {http.Error(w, "Failed to marshal request body", http.StatusInternalServerError)return}// 创建 HTTP 请求req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))if err != nil {http.Error(w, "Failed to create request", http.StatusInternalServerError)return}req.Header.Set("Content-Type", "application/json")req.Header.Set("Authorization", "Bearer "+authToken)// 设置 HTTP 客户端client := &http.Client{Timeout: time.Second * 50}resp, err := client.Do(req)if err != nil {http.Error(w, "Failed to get response", http.StatusInternalServerError)return}defer resp.Body.Close()// 设置响应头,开启流式输出w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")w.Header().Set("Cache-Control", "no-cache")w.Header().Set("Connection", "keep-alive")// 确保 ResponseWriter 支持 Flusherflusher, ok := w.(http.Flusher)if !ok {http.Error(w, "Streaming unsupported", http.StatusInternalServerError)return}// 处理流式响应scanner := bufio.NewScanner(resp.Body)for scanner.Scan() {line := scanner.Text()// 处理以 "data: " 开头的行if strings.HasPrefix(line, "data: ") {line = strings.TrimPrefix(line, "data: ")}if line == "[DONE]" {break}if line == "" {continue}// 解析响应内容var chunk ResponseBodyif err := json.Unmarshal([]byte(line), &chunk); err != nil {continue}// 将响应数据逐步发送给客户端for _, choice := range chunk.Choices {content := choice.Delta.Content_, err := w.Write([]byte(content))if err != nil {http.Error(w, "Failed to write response", http.StatusInternalServerError)return}flusher.Flush() // 刷新缓冲区}}if err := scanner.Err(); err != nil {http.Error(w, "Scanner error", http.StatusInternalServerError)return}
}func main() {http.HandleFunc("/stream", StreamHandler)fmt.Println("Server started at :8080")http.ListenAndServe(":8080", nil)
}

核心流程

  • 接收到用户输入后,将其作为 content 参数发送给目标 API。
  • 开启流式输出模式,设置 Stream: true
  • 使用 http.Flusher 将从远程接口接收到的内容逐步发送给客户端。

关键点

  • 流式响应头设置

    go复制代码w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    
  • 实时输出: 通过 w.Write 输出内容后调用 flusher.Flush() 确保数据实时发送。

启动服务后,通过浏览器访问类似以下 URL:

http://localhost:8080/stream?content=Hello%20world

客户端会逐步接收内容,类似命令行实时打印。

1. HTTP 协议中的流式响应

流式输出利用 HTTP 协议的特性,不关闭连接,逐步将数据发送给客户端。典型流式响应会设置如下 HTTP Header:

  • Content-Type: text/event-stream
    表示这是一个事件流(Event Stream),用于向客户端连续发送数据片段。
  • Cache-Control: no-cache
    防止响应被缓存,以确保客户端接收到实时内容。
  • Connection: keep-alive
    保持连接处于活跃状态,支持多次数据传输。
2. 流式输出的工作原理
  1. 客户端发起请求,服务器在接收到请求后开始响应。
  2. 服务器不一次性生成完整的响应内容,而是将生成的部分数据逐段发送。
  3. 客户端收到数据后立即处理,而无需等待完整响应结束。
  4. 在数据发送完成后,服务器可以选择关闭连接或保持连接以发送后续数据。

流式输出的常见应用场景

  1. 实时聊天:聊天模型逐词/逐句生成时,可以实时传输数据。
  2. 日志监控:将服务器的实时日志逐行推送到前端。
  3. 流式文件传输:如大文件或视频流传输。
  4. 实时进度更新:如任务进度条更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/61848.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用VisualStudio编写C++程序输出helloWorld

文章目录 1. C简介1.1 历史背景1.2 特点1.3 应用领域 2. 操作过程和代码2.1 打开Visual Studio(默认你下载了C的相关文件)2.2 创建新项目2.3 输入名字,创建2.4 右击源文件->添加->新建项2.5 命名好,进行添加2.6 输入代码2.7 输出结果 3. 总结 1. C…

万能的无人机锁定目标投放程序

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

LayaBox1.8.4实现UV滚动

实现思路: 在片元着色器采样时,增加一个随时间变化的偏移值,由于uv是一个二维向量所以加的偏移值也需要一个二维向量。注意:在Laya的 shader中除了0,输入其它数字必须输入带有小数的数字,否则报错 。 &quo…

Next.js- App Router 概览

#题引:我认为跟着官方文档学习不会走歪路 一:App Router与Page Router 在 v13 版本中,Next.js 引入了一个基于 React 服务器组件 构建的新的 App Router,而在这之前,Next.js 使用的是Page Router。 目录结构 pages …

milvus es

ES 与 Milvus 结合实现高效文档搜索的实战指南 原文链接 目录 背景介绍场景与效果概述架构对比与问题分析Milvus 向量搜索架构ES Milvus 搜索架构详细流程解析Milvus 搜索配置详解ES 搜索策略与 DSL 配置结果合并与排序策略总结与未来优化 1. 背景介绍 随着团队和公司的发…

Flutter 设计模式全面解析:抽象工厂

设计模式作为软件开发中的经典解决方案,在 Flutter 的开发中也能为我们提供强大的架构支持。本文来介绍一下如何在 Flutter 中来实现抽象工厂设计模式,以及如何创建一系列相关或依赖对象并优雅地管理它们之间的复杂依赖关系。 日常开发中我们也能经常看…

『 Linux 』网络层 - IP协议 (二)

文章目录 路由NAT技术分片与组装分片的组装IP协议分片的短板 路由 通常情况路由器具备了一个非常重要的功能,即构建子网; 同时路由器需要实现跨网络通信,说明路由器必须存在两个或以上的IP地址,通常在路由器中可以看到几个接口,分别是一个WAN口和几个LAN口; WAN口IP被称为公网I…

深度学习实战图像缺陷修复

这里写目录标题 概述1. 图像缺陷修复的研究背景2. 传统图像缺陷修复方法的局限性(1) 基于纹理合成的方法(2) 基于偏微分方程(PDE)的方法 3. 深度学习在图像缺陷修复中的兴起(1) 深度学习的基本思路(2) 深度学习方法的优势(3) 关键技术的引入 4. 深度学习…

【SQL实验】索引操作(菜单操作和命令操作)

【代码是自己的解答,并非标准答案,也有可能写错,文中可能会有不准确或待完善之处,恳请各位读者不吝批评指正,共同促进学习交流】 文件”成绩管理”导入【具体操作前几篇文章详细展示过来,这里跳过。还是不太…

[pdf,epub]162页《分析模式》漫谈合集01-35提供下载

《分析模式》漫谈合集01-35的pdf、epub文件,已上传至本号的CSDN资源。 如果CSDN资源下载有问题,可到umlchina.com/url/ap.html。 已排版成适合手机阅读,pdf的排版更好一些。 ★UMLChina为什么叒要翻译《分析模式》? ★[缝合故事…

【Linux学习】【Ubuntu入门】1-7 ubuntu下磁盘管理

1.准备一个U盘或者SD卡(插上读卡器),将U盘插入主机电脑,右键点击属性,查看U盘的文件系统确保是FAT32格式 2.右键单击ubuntu右下角图标,将U盘与虚拟机连接 参考链接 3. Ubuntu磁盘文件:/dev/s…

移远通信推出全新5G RedCap模组RG255AA系列,以更高性价比加速5G轻量化大规模商用

11月20,全球领先的物联网整体解决方案供应商移远通信宣布,正式推出其全新5G RedCap模组RG255AA系列。该系列模组支持5G NR独立组网(SA)和LTE Cat 4双模通信,具有高性能高集成度、低功耗、小尺寸、高性价比等优势&#…

数据集-目标检测系列- 花卉 玫瑰 检测数据集 rose >> DataBall

数据集-目标检测系列- 花卉 玫瑰 检测数据集 rose >> DataBall DataBall 助力快速掌握数据集的信息和使用方式,会员享有 百种数据集,持续增加中。 贵在坚持! 数据样例项目地址: * 相关项目 1)数据集可视化项…

GitHub 开源项目 Puter :云端互联操作系统

每天面对着各种云盘和在线应用,我们常常会遇到这样的困扰。 文件分散在不同平台很难统一管理,付费订阅的软件越来越多,更不用说那些烦人的存储空间限制了。 最近在 GitHub 上发现的一个开源项目 Puter 彻底改变了我的在线办公方式。 让人惊…

Python 使用 OpenCV 将 MP4 转换为 GIF图

以下是使用 Python 和 OpenCV 将 MP4 转换为 GIF 的示例代码: python import cv2 import imageiodef mp4_to_gif(mp4_path, gif_path, fps10, start_timeNone, end_timeNone):"""将MP4视频转换为GIF动图。:param mp4_path: 输入MP4视频的路径。:pa…

el-table的树形结构后端返回的id没有唯一键怎么办

前端自己生成唯一键 首先尝试了表格的几个字段用-拼接成唯一键 但是仍报错 只好自己利用uuid库生成;

【Linux】缓冲区/磁盘inode/动静态库

目录 一、缓冲区 (一)概念 (二)刷新策略 (三)仿写FILE (四)内核缓冲区 二、磁盘 (一)磁盘的存储 (二)磁盘的抽象存储结构 &am…

SpringBoot(9)-Dubbo+Zookeeper

目录 一、了解分布式系统 二、RPC 三、Dubbo 四、SpringBootDubboZookeeper 4.1 框架搭建 4.2 实现RPC 一、了解分布式系统 分布式系统:由一组通过网络进行通信,为了完成共同的任务而协调工作的计算机节点组成的系统 二、RPC RPC:远程…

【Github】如何使用Git将本地项目上传到Github

【Github】如何使用Git将本地项目上传到Github 写在最前面1. 注册Github账号2. 安装Git工具配置用户名和邮箱仅为当前项目配置(可选) 3. 创建Github仓库4. 获取仓库地址5. 本地操作(1)进入项目文件夹(2)克隆…

Spring:Spring整合Mybatis开发之纯Mybatis开发

目前我们已经对Spring有一个简单的认识了: Spring有一个容器,叫做IoC容器,里面保存bean。 在进行企业级开发的时候,其实除了将自己写的类让Spring管理之外,还有一部分重要的工作就是使用第三方的技术。前面已经讲了如何…