简介
Server-Sent Events (SSE) 技术是浏览器向服务器发送请求并保持长连接的技术,服务器通过长连接将数据推送到浏览器。SSE通常用于实时更新网页内容或获得服务器推送的通知。
体验过 ChatGPT 官方 Web 工具的同学, 应该了解, AI 对话返回的结果, 不是一次性返回的, 而是会在一段时间内, 持续输出, 就像是人在说话时, 有序地说出每一个字一样.
它是如何实现的呢, 通过 Chrome 检查工具, 可以看到, 它返回的内容类型 (Content-Type) 是 text/event-stream
, 而不是我们常用的 application/json
下面是实现一个基于事件驱动的SSE程序的步骤
1、创建一个HTTP服务器。
2、注册一个路由处理程序,用于处理SSE请求。
3、在路由处理程序中,设置响应头Content-Type为"text/event-stream",并且设置响应头Cache-Control为"no-cache"。
4、将事件写入响应中,用分隔符"\n\n"分隔不同事件的数据。
Go示例
路由部分
//app.aigupiao.com api路由 apiGroup := Router.Group("/goapi") { apiGroup.GET("/ai/sse", ai.SSE) }
主体实现
func SSE(ctx *gin.Context) { // 设置响应头,指定事件源的连接超时时间 ctx.Writer.Header().Set("Content-Type", "text/event-stream") ctx.Writer.Header().Set("Cache-Control", "no-cache") ctx.Writer.Header().Set("Connection", "keep-alive") ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*") // SSE格式的数据流 data := "data: Hello, world!\n\n" i := 0 for { if i > 2 { break } i++ time.Sleep(2 * time.Second) fmt.Fprintf(ctx.Writer, data) ctx.Writer.Flush() } }
这是一个简单的SSE服务器实现,可以在本地启动一个SSE服务器,在浏览器中访问"http://localhost:8080/ai/sse"即可订阅事件。
客户端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SSE test</title> <script type="text/javascript"> const es = new EventSource("/sse/ai/sse"); es.onmessage = function (e) { document.getElementById("test") .insertAdjacentHTML("beforeend", "<li>" + e.data + "</li>"); console.log(e); } // es.addEventListener("test-event", (e) => { // document.getElementById("test") // .insertAdjacentHTML("beforeend", "<li>" + e.data + "</li>"); // }); es.onerror = function (e) { // readyState说明 // 0:浏览器与服务端尚未建立连接或连接已被关闭 // 1:浏览器与服务端已成功连接,浏览器正在处理接收到的事件及数据 // 2:浏览器与服务端建立连接失败,客户端不再继续建立与服务端之间的连接 console.log("readyState = " + e.currentTarget.readyState); } </script> </head> <body> <h1>SSE test</h1> <div> <ul id="test"> </ul> </div> </body> </html>
问题注意事项
开发、测试还是挺顺利的, 效果也正常, 但发到线上以后, 一直 loading 转圈, 没有输出, 打开 Chrome 检查工具, 发现请求一直处于 pending
状态... 最后所有内容一起输出
反复查资料, 对比别人的实现; 怀疑是 CORS 跨域问题; 弄到同一个域名下也是一样的问题,突然想到是不是 nginx 反向代理配置的问题, 我的域名网址 www.phpmianshi.com, 是通过 nginx 反向代理到golang的服务端口的. 要验证这个猜想也很简单, 直接用线上gin项目的 ip+port 端口访问测试一下, 果然没有问题!!!
下面是 sse 和 websocket 常用的nginx配置,大家参考,配置后一切正常~~
#eventSource location /es/ { proxy_pass http://请求地址/; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; } #webSocket location /api/ws/ { proxy_pass http://请求地址/; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
《本文》有 0 条评论