//后端代码 //注意 **我注释的代码,是不使用gin框架封装的Stream方法,也就是C.Stream(func())和C.ssevent(),只是C.Stream要改成for循环持续的从通道里面进行读,直到通道关闭,结束for循环** package main import ( "catface/app/service/nlp/glm" _ "catface/bootstrap" "fmt" "io" "testing" // "time" "github.com/gin-gonic/gin" ) func SSE(c *gin.Context) { // 设置响应头,告诉前端适用event-stream事件流交互 //c.Writer.Header().Set("Content-Type", "text/event-stream") //c.Writer.Header().Set("Cache-Control", "no-cache") //c.Writer.Header().Set("Connection", "keep-alive") // 判断是否支持sse //w := c.Writer //flusher, _ := w.(http.Flusher) query := c.Query("query") // 接收前端页面关闭连接通知 closeNotify := c.Request.Context().Done() // 开启协程监听前端页面是否关闭了连接,关闭连接会触发此方法 go func() { <-closeNotify fmt.Println("SSE关闭了") return }() //新建一个通道,用于数据接收和响应 Chan := make(chan string) // 异步接收GPT响应,然后把响应的数据发送到通道Chan go func() { err := glm.ChatStream(query, Chan) if err != nil { fmt.Println("Error", err) } close(Chan) }() // gin框架封装的stream,会持续的调用这个func方法,记得返回true;返回false代表结束调用func方法 c.Stream(func(w io.Writer) bool { select { case i, ok := <-Chan: if !ok { return false } c.SSEvent("chat", i) // c.SSEvent会自动修改响应头为事件流,并发送”test“事件流给前端监听”test“的回调方法 //flusher.Flush() // 确保立即发送 return true case <-closeNotify: fmt.Println("SSE关闭了") return false } }) } func TestSSE(t *testing.T) { engine := gin.Default() // 设置跨域中间件 engine.Use(func(context *gin.Context) { origin := context.GetHeader("Origin") // 允许 Origin 字段中的域发送请求 context.Writer.Header().Add("Access-Control-Allow-Origin", origin) // 这边我的前端页面在63342,会涉及跨域,这个根据自己情况设置,或者直接设置为”*“,放行所有的 // 设置预验请求有效期为 86400 秒 context.Writer.Header().Set("Access-Control-Max-Age", "86400") // 设置允许请求的方法 context.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, U`PDATE, PATCH") // 设置允许请求的 Header context.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Apitoken") // 设置拿到除基本字段外的其他字段,如上面的Apitoken, 这里通过引用Access-Control-Expose-Headers,进行配置,效果是一样的。 context.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Headers") // 配置是否可以带认证信息 context.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // OPTIONS请求返回200 if context.Request.Method == "OPTIONS" { fmt.Println(context.Request.Header) context.AbortWithStatus(200) } else { context.Next() } }) engine.GET("/admin/rag/default_talk", SSE) // TIP 记得适用get请求,我用post前端报404,资料说是SSE只支持get请求 engine.Run(":20201") }