Golang后端+React前端SSE流式输出延迟问题求助
排查SSE打字机效果失效问题
后端Go代码排查
从你控制台一次性输出token的现象来看,优先排查后端流式响应的配置和处理逻辑:
- 确认OpenAI流式调用开启:调用
CreateChatCompletion时必须设置Stream: true,否则OpenAI会返回完整响应而非流式输出。核心配置示例:
req := openai.ChatCompletionRequest{ Model: openai.GPT3Dot5Turbo, Messages: messages, Stream: true, // 必须开启此参数 }
- 检查流式响应的发送逻辑:开启流式后,需要遍历
ChatCompletionStream的返回通道,逐个发送token,同时强制刷新响应缓冲区,避免Go服务器缓冲内容。示例处理代码:
stream, err := client.CreateChatCompletionStream(ctx, req) if err != nil { // 错误处理逻辑 } defer stream.Close() // 配置SSE响应头,缺一不可 w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") w.Header().Set("Transfer-Encoding", "chunked") // 初始化响应刷新器,确保内容实时发送 flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming not supported", http.StatusInternalServerError) return } for { response, err := stream.Recv() if errors.Is(err, io.EOF) { fmt.Fprintf(w, "data: [DONE]\n\n") flusher.Flush() break } if err != nil { fmt.Fprintf(w, "data: {\"error\":\"%s\"}\n\n", err.Error()) flusher.Flush() break } // 提取单个token并按SSE格式发送 token := response.Choices[0].Delta.Content if token != "" { fmt.Fprintf(w, "data: %s\n\n", token) flusher.Flush() // 强制刷新,避免服务器缓冲 } }
- 排查中间件干扰:检查是否有日志、压缩类中间件对该路由启用了响应缓冲,这类中间件会把流式内容攒成整块输出,需要为
/answer/api/v1/chat/completion路由跳过缓冲配置。
前端React代码排查
如果后端通过curl测试(curl -N http://你的服务器地址/answer/api/v1/chat/completion)能看到token逐个输出,再排查前端逻辑:
- 正确监听SSE消息:确保在
onmessage事件中实时更新UI,而非收集所有内容后再渲染。示例代码:
const [message, setMessage] = useState(''); useEffect(() => { const eventSource = new EventSource('/answer/api/v1/chat/completion'); eventSource.onmessage = (event) => { if (event.data === '[DONE]') { eventSource.close(); return; } // 实时追加token到现有内容 setMessage(prev => prev + event.data); }; eventSource.onerror = (error) => { console.error('SSE error:', error); eventSource.close(); }; return () => eventSource.close(); }, []);
- 处理React状态批量更新:如果遇到状态更新延迟,可使用
flushSync强制同步更新:
import { flushSync } from 'react-dom'; // 在onmessage事件内: flushSync(() => { setMessage(prev => prev + event.data); });
- 排除开发工具干扰:检查是否有开发服务器插件、浏览器缓存插件导致响应被缓冲,可直接在无痕模式下测试。
核心注意事项
- 后端必须保证
Transfer-Encoding: chunked头正确设置,且每次发送token后调用flusher.Flush(),否则Go服务器会缓冲内容直到缓冲区满才发送。 - SSE消息格式必须严格遵循
data: 内容\n\n的规则,末尾的两个换行符是触发前端onmessage事件的关键。 - 绝对不能在后端将所有token收集为字符串后一次性发送,这是流式效果失效的最常见原因。
内容的提问来源于stack exchange,提问作者jakelevirne
相关产品推荐
相关产品推荐

