AI 聊天,会用到流式返回的接口。用 goframe 框架尝试尝试。
开始
初始化项目
gf init stream.demo -u
cd stream.demo
gf run main.go
默认路由信息是这样的。
build: main.go
go build -o .\main.exe main.go
.\main.exe
build running pid: 44364
2025-07-11T13:44:19.160+08:00 [INFO] pid[44364]: http server started listening on [:8000]
2025-07-11T13:44:19.160+08:00 [INFO] {ac79854f201c51188f16b244dba07b30} swagger ui is serving at address: http://127.0.0.1:8000/swagger/
2025-07-11T13:44:19.161+08:00 [INFO] {ac79854f201c51188f16b244dba07b30} openapi specification is serving at address: http://127.0.0.1:8000/api.json
ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | ALL | /api.json | github.com/gogf/gf/v2/net/ghttp.(*Server).openapiSpec |
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | GET | /hello | stream.demo/internal/controller/hello.(*ControllerV1).Hello | ghttp.MiddlewareHandlerResponse
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | ALL | /swagger/* | github.com/gogf/gf/v2/net/ghttp.(*Server).swaggerUI | HOOK_BEFORE_SERVE
----------|--------|------------|-------------------------------------------------------------|----------------------------------
提交版本
git init -b main
git add .
git commit -m 'initialize'
定义接口,创建控制器,定义路由
新建 api/chat/v1/chat.go
文件,定义以下请求和返回
package v1
import "github.com/gogf/gf/v2/frame/g"
type ChatReq struct {
g.Meta `path:"/chat" method:"post" tags:"聊天" summary:"聊天请求"`
Uid uint64 `v:"required#用户ID不能为空" json:"uid" dc:"用户ID"`
Message string `v:"required#消息内容不能为空" json:"message" dc:"消息内容"`
}
type ChatRes struct {
IsEnd bool `json:"is_end" dc:"是否结束"`
}
执行 gf gen ctrl 生成控制器。修改 internal/cmd/cmd.go
,增加路由配置。
import (
……
"stream.demo/internal/controller/hello"
)
……
s.Group("/api", func(group *ghttp.RouterGroup) {
group.Bind(
chat.NewV1(),
)
})
再运行 gf run main.go,把项目跑起来看看。发现 chat 路由已经好了。
build: main.go
go build -o .\main.exe main.go
.\main.exe
build running pid: 44148
2025-07-11T14:13:29.676+08:00 [INFO] pid[44148]: http server started listening on [:8000]
2025-07-11T14:13:29.676+08:00 [INFO] {5065f8ccb71d5118823d646fdde40224} swagger ui is serving at address: http://127.0.0.1:8000/swagger/
2025-07-11T14:13:29.677+08:00 [INFO] {5065f8ccb71d5118823d646fdde40224} openapi specification is serving at address: http://127.0.0.1:8000/api.json
ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | ALL | /api.json | github.com/gogf/gf/v2/net/ghttp.(*Server).openapiSpec |
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | POST | /api/chat | stream.demo/internal/controller/chat.(*ControllerV1).Chat | ghttp.MiddlewareHandlerResponse
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | GET | /hello | stream.demo/internal/controller/hello.(*ControllerV1).Hello | ghttp.MiddlewareHandlerResponse
----------|--------|------------|-------------------------------------------------------------|----------------------------------
:8000 | ALL | /swagger/* | github.com/gogf/gf/v2/net/ghttp.(*Server).swaggerUI | HOOK_BEFORE_SERVE
----------|--------|------------|-------------------------------------------------------------|----------------------------------
开始流式 demo
编辑 internal/controller/chat/chat_v1_chat.go
package chat
import (
"context"
"fmt"
"time"
"github.com/gogf/gf/v2/net/ghttp"
v1 "stream.demo/api/chat/v1"
)
func (c *ControllerV1) Chat(ctx context.Context, req *v1.ChatReq) (res *v1.ChatRes, err error) {
r := ghttp.RequestFromCtx(ctx)
r.Response.Header().Set("Content-Type", "text/event-stream")
r.Response.Header().Set("Cache-Control", "no-cache")
r.Response.Header().Set("Connection", "keep-alive")
r.Response.Header().Set("Content-Encoding", "identity")
for i := range 10 {
data := fmt.Sprintf(`{"uid":%d, "message":"%s","index":%d}`, req.Uid, req.Message, i)
r.Response.Writefln("data: %s\n\n", data)
r.Response.Flush()
time.Sleep(time.Millisecond * 1000)
}
fmt.Println("发送结束")
res = &v1.ChatRes{}
res.IsEnd = true
return
}
重新运行项目。
测试
可以使用 curl 命令,可以使用 apifox
这种工具,也可以使用浏览器。 这里使用浏览器加 js 的方式来搞。
使用 chrome 打开 http://127.0.0.1:8000 ,然后打开控制台。输入以下测试代码,回车。
fetch("http://127.0.0.1:8000/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: "舞低杨柳楼心月,歌尽桃花扇底风",
uid: 1,
}),
})
.then(readStream)
.catch(console.error);
async function readStream(res) {
const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let lines = buffer.split("\n\n");
buffer = lines.pop();
for (const chunk of lines) {
const dataLine = chunk.trim();
if (dataLine.startsWith("data: ")) {
const data = dataLine.slice(6);
console.log(getNowTime(), "消息:", data);
}
}
}
}
function getNowTime() {
const date = new Date();
return date.toLocaleTimeString("zh-CN", {
hour12: false,
timeZone: "Asia/Shanghai",
});
}
截图如下:
提交版本控制。
git add .
git commit -m '增加 chat 接口,并测试流式响应'
nginx 处理
如果走 nginx
代理,需要处理关闭相应缓冲
location /api/ {
proxy_pass http://你的后端服务地址;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
chunked_transfer_encoding on;
}