Hertz v0.10.0 版本新增两项重要功能并修复了一些问题。
1. SSE 协议支持
SSE(Server-Sent Events) 是一种允许服务器主动向浏览器推送实时数据的技术。它基于普通的 HTTP 协议,适合需要服务器单向发送消息的场景(比如新闻推送、股票价格更新、实时通知等)。
SSE 天然支持服务器向客户端的单向数据流,当大语言模型需要实时获取文档更新、工具调用结果时,SSE 无需客户端轮询即可实现事件驱动的数据推送。因此目前流行大语言模型的 MCP (Model Context Protocol) 或 A2A (Agent2Agent) 协议都依赖于 SSE 协议。
Server 端实现
返回数据
import "github.com/cloudwego/hertz/pkg/protocol/sse"
func HandleSSE(ctx context.Context, c *app.RequestContext) {
println("Server Got LastEventID", sse.GetLastEventID(&c.Request))
w := sse.NewWriter(c)
for i := 0; i < 5; i++ {
w.WriteEvent("id-x", "message", []byte("hello\n\nworld"))
time.Sleep(10 * time.Millisecond)
}
w.Close() // 发送最后的 chunk 数据,确保优雅退出。可选,Hertz 在 Handler 返回后会自动调用。
// 请确保 writer 的生命周期和 handler 一致。不要 go 异步后台使用。
}
Client 端实现
发起请求
与正常 Hertz 发起一个普通的 http 请求完全一致。
注意:新 hertz 版本会自动识别 SSE 流,不用显式设置 WithResponseBodyStream(true)
部分可选 Header
import "github.com/cloudwego/hertz/pkg/protocol/sse"
sse.AddAcceptMIME(req) // 部分 SSE Server 可能会要求显式增加 Accept: text/event-stream
sse.SetLastEventID(req, "id-123") // 对于有状态服务,需要通过 SetLastEventID 告诉 Server
处理返回
import "github.com/cloudwego/hertz/pkg/protocol/sse"
func HandleSSE(ctx context.Context, resp *protocol.Response) error {
r, err := sse.NewReader(resp)
if err != nil {
return err
}
// 也可以手动调用 r.Read 方法
err = r.ForEach(ctx, func(e *Event) error {
println("Event:", e.String())
return nil
})
if err != nil { // 如果 Server 正常断开,这里 err == nil,不会报错
// 其他 io 错误 或 ctx cancelled
return err
}
println("Client LastEventID", r.LastEventID()) // 可用于保存最后接收的 Event ID
return nil
}
在Hertz中使用SSE方法请参阅 https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/sse/。
2. 适配器
添加 http.Handler 适配器,使用官方 net/http 生态系统扩展 Hertz。
-
允许用户将现有的 http.HandlerFunc 方法直接转为 HertzHandler。 -
同时可以直接使用 http.FileServer embed.FS 等标准库方法 -
甚至可以直接在 Hertz 使用 github.com/gorilla/websocket
具体例子
package main
import (
"embed"
"net/http"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/adaptor"
)
//go:embed static/*
var staticFiles embed.FS
func main() {
h := server.Default()
helloHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello hertz!"))
})
h.GET("/hello", adaptor.HertzHandler(helloHandler))
staticFS := adaptor.HertzHandler(http.FileServer(http.FS(staticFiles)))
h.GET("/static/*filepath", staticFS)
h.HEAD("/static/*filepath", staticFS)
h.Spin()
}
详细变更
Feature
-
#1327 feat(adaptor): 为 http.Handler 添加新的 HertzHandler -
#1349 feat(sse): SetLastEventID -
#1343 feat(sse): reader 支持取消流 -
#1341 feat(server): 检测请求 race -
#1339 feat(sse): 添加 LastEventID helper -
#1335 feat(protocol): 新的 sse 包 -
#1322 feat: server 使用标准 go net 传输时感知客户端连接关闭
Fix
-
#1340 fix:仅在 amd64/arm64 linux/darwin 上使用 netpoll 和 sonic -
#1333 fix(protocol): 非预期的设置 resp.bodyStream -
#1329 fix(client): sse 场景下自动切换为 stream body 模式 -
#1332 fix(server): server 关闭时检查 ExitWaitTimeout -
#1316 fix: 优先使用自定义 validator
Tests
-
#1336 test(protocol): 修复硬编码的监听地址
Chore
本文由 mdnice 多平台发布