From b2210e3a7f2284facdad28cd2d37a8d9f0eef62c Mon Sep 17 00:00:00 2001 From: "qiheng.zhou" Date: Wed, 21 May 2025 17:31:53 +0800 Subject: [PATCH 1/4] docs: add doc for hertz v0.10.0 --- .../en/blog/releases/Hertz/release-v0_10_0.md | 36 ++++++ .../tutorials/basic-feature/http-adaptor.md | 47 ++++++++ .../docs/hertz/tutorials/basic-feature/sse.md | 103 +++++++++++++++++ .../zh/blog/releases/Hertz/release-v0_10_0.md | 36 ++++++ .../tutorials/basic-feature/http-adaptor.md | 47 ++++++++ .../docs/hertz/tutorials/basic-feature/sse.md | 104 ++++++++++++++++++ 6 files changed, 373 insertions(+) create mode 100644 content/en/blog/releases/Hertz/release-v0_10_0.md create mode 100644 content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md create mode 100644 content/en/docs/hertz/tutorials/basic-feature/sse.md create mode 100644 content/zh/blog/releases/Hertz/release-v0_10_0.md create mode 100644 content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md create mode 100644 content/zh/docs/hertz/tutorials/basic-feature/sse.md diff --git a/content/en/blog/releases/Hertz/release-v0_10_0.md b/content/en/blog/releases/Hertz/release-v0_10_0.md new file mode 100644 index 00000000000..bae82a8a8b8 --- /dev/null +++ b/content/en/blog/releases/Hertz/release-v0_10_0.md @@ -0,0 +1,36 @@ +--- +title: "Hertz Release v0.10.0" +linkTitle: "Release v0.10.0" +projects: ["Hertz"] +date: 2025-05-21 +description: > +--- + +The Hertz v0.10.0 release adds two features and some fixes. + +1. Integrate SSE functionality. Refer to [SSE](/docs/hertz/tutorials/basic-feature/sse) for usage. +2. Added http.Handler adaptor, extending Hertz using the official net/http ecosystem. Refer to [Adaptor](/docs/hertz/tutorials/basic-feature/http-adaptor) for usage. + +## Feature +1. [[#1327](https://github.com/cloudwego/hertz/pull/1327)] feat(adaptor): new HertzHandler for http.Handler +2. [[#1349](https://github.com/cloudwego/hertz/pull/1349)] feat(sse): SetLastEventID +3. [[#1343](https://github.com/cloudwego/hertz/pull/1343)] feat(sse): reader supports cancel stream +4. [[#1341](https://github.com/cloudwego/hertz/pull/1341)] feat(server): detect request race +5. [[#1339](https://github.com/cloudwego/hertz/pull/1339)] feat(sse): add LastEventID helpers +6. [[#1335](https://github.com/cloudwego/hertz/pull/1335)] feat(protocol): new sse pkg +7. [[#1322](https://github.com/cloudwego/hertz/pull/1322)] feat: std transport sense client connection close + +## Fix +1. [[#1340](https://github.com/cloudwego/hertz/pull/1340)] fix: only use netpoll & sonic on amd64/arm64 linux/darwin +2. [[#1333](https://github.com/cloudwego/hertz/pull/1333)] fix(protocol): unexpected set resp.bodyStream +3. [[#1329](https://github.com/cloudwego/hertz/pull/1329)] fix(client): stream body for sse instead of timeout +4. [[#1332](https://github.com/cloudwego/hertz/pull/1332)] fix(server): Shutdown checks ExitWaitTimeout +5. [[#1316](https://github.com/cloudwego/hertz/pull/1316)] fix: prioritize custom validator + +## Tests +1. [[#1336](https://github.com/cloudwego/hertz/pull/1336)] test(protocol): fix hardcoded listen addr + +## Chore +1. [[#1353](https://github.com/cloudwego/hertz/pull/1353)] chore: update netpoll dependency +2. [[#1337](https://github.com/cloudwego/hertz/pull/1337)] chore(hz): update hz tool v0.9.7 +3. [[#1328](https://github.com/cloudwego/hertz/pull/1328)] ci: disable codecov annotations diff --git a/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md b/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md new file mode 100644 index 00000000000..3d6a6f0264d --- /dev/null +++ b/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md @@ -0,0 +1,47 @@ +--- +title: "Hertz http.Handler adaptor" +date: 2025-05-21 +weight: 12 +keywords: ["adaptor"] +description: "Hertz http.Handler adaptor" +--- + +## Background +- We will deprecate the Hertz's native file system +- Provide an fs implementation that is both compatible and performs well, using the official net/http ecosystem. +- Reduce the implementation of a large number of extensions by expanding the hertz function through the official net/http ecosystem + +## What is adaptor.HertzHandler +- You are allowed to directly convert your existing http.HandlerFunc method into a HertzHandler. +- You can directly use http.FileServer, embed.FS, and other official ecosystems +- You can even use this directly at Hertz github.com/gorilla/websocket + +## Example +```go +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() +} +``` diff --git a/content/en/docs/hertz/tutorials/basic-feature/sse.md b/content/en/docs/hertz/tutorials/basic-feature/sse.md new file mode 100644 index 00000000000..508cebed920 --- /dev/null +++ b/content/en/docs/hertz/tutorials/basic-feature/sse.md @@ -0,0 +1,103 @@ +--- +title: "SSE" +date: 2025-05-21 +weight: 12 +keywords: ["SSE"] +description: "Hertz Support SSE。" +--- +```Code:https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` + +**Changelog** +- 2025/05/15 Has been released to the open-source version v0.10.0 +- 2025/05/13 [d3c68463] Added Get / SetLastEventID. +- 2025/05/13 [3952e956] The Reader ForEach interface supports passing in ctx. +- 2025/04/24 [cf38573ce] First version completed + +## 什么是Server-Sent Events (SSE) +- SSE is an HTML standard used to describe the implementation of the EventSource API in browsers. Strictly speaking, it is not an HTTP protocol. +- Both the popular Model Context Protocol (MCP) and Agent2Agent (A2A) protocols are based to some extent on SSE + - https://www.anthropic.com/news/model-context-protocol + - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ + +## Server Implementation +### Return data +```go +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异步后台使用。 +} +``` + + +### FAQ +#### connection has been closed when flush +Reason: The client connection has been disconnected, possibly due to a long time without response data. Please check the corresponding configuration. + + +## Client Implementation + +### Initiate a request +It's exactly the same as making a normal HTTP request to Hertz. +Note: The new Hertz version will automatically recognize SSE streams, so you don't need to explicitly set `WithResponseBodyStream(true)` + +**Some optional headers** +```go +import "github.com/cloudwego/hertz/pkg/protocol/sse" + +sse.AddAcceptMIME(req) // 部分SSE Server可能会要求显式增加 Accept: text/event-stream +sse.SetLastEventID(req, "id-123") // 对于有状态服务,需要通过 SetLastEventID 告诉 Server +``` + +### Handle response +```go +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 +} +``` + +### FAQ + +#### How to implement multi-level SSE/SSE Proxy? +- You can refer to the implementations on both the Client and Server sides simultaneously +- Enable `WithSenseClientDisconnection` in the server and pass the context to the next hop's Reader ForEach + - In this way, when the Client disconnects, it will be automatically recognized, and the entire stream will be interrupted + +## Event Struct +```go +// Event represents a Server-Sent Event (SSE). +type Event struct { + ID string // 即 Event ID,sse.Reader 会自动记录最后的 Event ID,可使用 LastEventID() 获取 + Type string // 即 Event Type,常见的如 "message" + Data []byte // 为了方便使用 Unmarshal/Marshal,这里使用 []byte,但是按spec这个字段必须要 utf8 string + + // 不建议使用,主要是针对浏览器的 SourceEvent 返回该字段控制其重试策略。 + // 如果使用Hertz作为Client可以参考:https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ + Retry time.Duration +} +``` diff --git a/content/zh/blog/releases/Hertz/release-v0_10_0.md b/content/zh/blog/releases/Hertz/release-v0_10_0.md new file mode 100644 index 00000000000..e251e9f0d62 --- /dev/null +++ b/content/zh/blog/releases/Hertz/release-v0_10_0.md @@ -0,0 +1,36 @@ +--- +title: "Hertz Release v0.10.0" +linkTitle: "Release v0.10.0" +projects: ["Hertz"] +date: 2025-05-21 +description: > +--- + +Hertz v0.10.0 版本新增两项功能并修复了一些问题。 + +1. 集成 SSE 功能。使用方法请参阅 [SSE](/docs/hertz/tutorials/basic-feature/sse)。 +2. 添加 http.Handler 适配器,使用官方 net/http 生态系统扩展 Hertz。使用方法请参阅 [Adaptor](/docs/hertz/tutorials/basic-feature/http-adaptor)。 + +## Feature +1. [[#1327](https://github.com/cloudwego/hertz/pull/1327)] feat(adaptor): 为 http.Handler 添加新的 HertzHandler +2. [[#1349](https://github.com/cloudwego/hertz/pull/1349)] feat(sse): SetLastEventID +3. [[#1343](https://github.com/cloudwego/hertz/pull/1343)] feat(sse): reader 支持取消流 +4. [[#1341](https://github.com/cloudwego/hertz/pull/1341)] feat(server): 检测请求 race +5. [[#1339](https://github.com/cloudwego/hertz/pull/1339)] feat(sse): 添加 LastEventID helper +6. [[#1335](https://github.com/cloudwego/hertz/pull/1335)] feat(protocol): 新的 sse 包 +7. [[#1322](https://github.com/cloudwego/hertz/pull/1322)] feat: server 使用标准 go net 传输时感知客户端连接关闭 + +## Fix +1. [[#1340](https://github.com/cloudwego/hertz/pull/1340)] fix:仅在 amd64/arm64 linux/darwin 上使用 netpoll 和 sonic +2. [[#1333](https://github.com/cloudwego/hertz/pull/1333)] fix(protocol): 非预期的设置 resp.bodyStream +3. [[#1329](https://github.com/cloudwego/hertz/pull/1329)] fix(client): sse 场景下自动切换为 stream body 模式 +4. [[#1332](https://github.com/cloudwego/hertz/pull/1332)] fix(server): server 关闭时检查 ExitWaitTimeout +5. [[#1316](https://github.com/cloudwego/hertz/pull/1316)] fix: 优先使用自定义 validator + +## Tests +1. [[#1336](https://github.com/cloudwego/hertz/pull/1336)] test(protocol): 修复硬编码的监听地址 + +## Chore +1. [[#1353](https://github.com/cloudwego/hertz/pull/1353)] chore:更新 netpoll 依赖 +2. [[#1337](https://github.com/cloudwego/hertz/pull/1337)] chore(hz): 更新 hz 工具 v0.9.7 +3. [[#1328](https://github.com/cloudwego/hertz/pull/1328)] ci: 禁用 codecov 注释 diff --git a/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md b/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md new file mode 100644 index 00000000000..b3721edb12f --- /dev/null +++ b/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md @@ -0,0 +1,47 @@ +--- +title: "Hertz http.Handler adaptor" +date: 2025-05-21 +weight: 12 +keywords: ["adaptor"] +description: "Hertz http.Handler adaptor" +--- + +## 背景 +- 主淘汰长期没有良好维护的 hertz 原生 fs 实现; +- 通过官方 net/http 生态提供一个兼容性较好,性能也不差的 fs 实现。 +- 通过官方 net/http 生态扩展 hertz 功能,减少大量扩展实现 + +## 什么是 adaptor.HertzHandler +- 允许你将现有的 http.HandlerFunc 方法直接转为 HertzHandler +- 可以直接使用 http.FileServer embed.FS 等官方生态 +- 甚至可以直接在 Hertz 使用 github.com/gorilla/websocket + +## 具体例子 +```go +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() +} +``` diff --git a/content/zh/docs/hertz/tutorials/basic-feature/sse.md b/content/zh/docs/hertz/tutorials/basic-feature/sse.md new file mode 100644 index 00000000000..e163cd7e24d --- /dev/null +++ b/content/zh/docs/hertz/tutorials/basic-feature/sse.md @@ -0,0 +1,104 @@ +--- +title: "SSE" +date: 2025-05-21 +weight: 12 +keywords: ["SSE"] +description: "Hertz 提供的 SSE 能力。" +--- +```代码:https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` + +**Changelog** +- 2025/05/15 已发布到开源版本 v0.10.0 +- 2025/05/13 [d3c68463] 增加 Get / SetLastEventID +- 2025/05/13 [3952e956] Reader ForEach 接口支持传入 ctx +- 2025/04/24 [cf38573ce] 第一版本完成 + +## 什么是Server-Sent Events (SSE) +- SSE是一个HTML标准,用于描述浏览器 EventSource API 的实现。严格说,不是http协议。 +- 流行的 Model Context Protocol (MCP) 或 Agent2Agent (A2A) 协议都一定程度基于 SSE + - https://www.anthropic.com/news/model-context-protocol + - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ + +## Server端实现 +### 返回数据 +```go +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异步后台使用。 +} +``` + + +### 常见问题 +#### connection has been closed when flush +原因: +Client连接断开,可能是太久没有响应数据,请检查相应配置。 + + +## Client端实现 + +### 发起请求 +与正常 Hertz 发起一个普通的http请求完全一致。 +注意:新hertz版本会自动识别SSE流,不用显式设置 WithResponseBodyStream(true) + +**部分可选 Header** +```go +import "github.com/cloudwego/hertz/pkg/protocol/sse" + +sse.AddAcceptMIME(req) // 部分SSE Server可能会要求显式增加 Accept: text/event-stream +sse.SetLastEventID(req, "id-123") // 对于有状态服务,需要通过 SetLastEventID 告诉 Server +``` + +### 处理返回 +```go +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 +} +``` + +### 常见问题 + +#### 如何实现多级的 SSE / SSE Proxy? +- 可以同时参考 Client 端和 Server端的实现 +- Server 启用 `WithSenseClientDisconnection` 并且把 context 传入 下一跳的 Reader ForEach + - 这样当Client断开时,会自动识别,并且中断整个流 + +## Event 结构体 +```go +// Event represents a Server-Sent Event (SSE). +type Event struct { + ID string // 即 Event ID,sse.Reader 会自动记录最后的 Event ID,可使用 LastEventID() 获取 + Type string // 即 Event Type,常见的如 "message" + Data []byte // 为了方便使用 Unmarshal/Marshal,这里使用 []byte,但是按spec这个字段必须要 utf8 string + + // 不建议使用,主要是针对浏览器的 SourceEvent 返回该字段控制其重试策略。 + // 如果使用Hertz作为Client可以参考:https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ + Retry time.Duration +} +``` From bebcbafd0a7c7101dfd09fee20a2c10bd2a07cc9 Mon Sep 17 00:00:00 2001 From: "qiheng.zhou" Date: Thu, 22 May 2025 10:37:16 +0800 Subject: [PATCH 2/4] fix: weight and translate --- .../hertz/tutorials/basic-feature/_index.md | 2 + .../tutorials/basic-feature/http-adaptor.md | 9 ++- .../docs/hertz/tutorials/basic-feature/sse.md | 55 +++++++++++-------- .../hertz/tutorials/basic-feature/_index.md | 2 + .../tutorials/basic-feature/http-adaptor.md | 11 ++-- .../docs/hertz/tutorials/basic-feature/sse.md | 51 ++++++++++------- 6 files changed, 79 insertions(+), 51 deletions(-) diff --git a/content/en/docs/hertz/tutorials/basic-feature/_index.md b/content/en/docs/hertz/tutorials/basic-feature/_index.md index 0812a62a9c5..64ca04eab38 100644 --- a/content/en/docs/hertz/tutorials/basic-feature/_index.md +++ b/content/en/docs/hertz/tutorials/basic-feature/_index.md @@ -23,6 +23,8 @@ keywords: "Constants", "Render", "JSON Marshal Library", + "SSE", + "http.Handler adaptor", ] description: "Basic feature of Hertz." --- diff --git a/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md b/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md index 3d6a6f0264d..16a8a799901 100644 --- a/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md +++ b/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md @@ -1,22 +1,25 @@ --- -title: "Hertz http.Handler adaptor" +title: "http.Handler adaptor" date: 2025-05-21 -weight: 12 +weight: 20 keywords: ["adaptor"] -description: "Hertz http.Handler adaptor" +description: "" --- ## Background + - We will deprecate the Hertz's native file system - Provide an fs implementation that is both compatible and performs well, using the official net/http ecosystem. - Reduce the implementation of a large number of extensions by expanding the hertz function through the official net/http ecosystem ## What is adaptor.HertzHandler + - You are allowed to directly convert your existing http.HandlerFunc method into a HertzHandler. - You can directly use http.FileServer, embed.FS, and other official ecosystems - You can even use this directly at Hertz github.com/gorilla/websocket ## Example + ```go package main diff --git a/content/en/docs/hertz/tutorials/basic-feature/sse.md b/content/en/docs/hertz/tutorials/basic-feature/sse.md index 508cebed920..b48197d3d6a 100644 --- a/content/en/docs/hertz/tutorials/basic-feature/sse.md +++ b/content/en/docs/hertz/tutorials/basic-feature/sse.md @@ -1,26 +1,30 @@ --- title: "SSE" date: 2025-05-21 -weight: 12 +weight: 19 keywords: ["SSE"] -description: "Hertz Support SSE。" +description: "SSE in Hertz" --- ```Code:https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` **Changelog** + - 2025/05/15 Has been released to the open-source version v0.10.0 - 2025/05/13 [d3c68463] Added Get / SetLastEventID. - 2025/05/13 [3952e956] The Reader ForEach interface supports passing in ctx. - 2025/04/24 [cf38573ce] First version completed -## 什么是Server-Sent Events (SSE) +## What is Server-Sent Events (SSE) + - SSE is an HTML standard used to describe the implementation of the EventSource API in browsers. Strictly speaking, it is not an HTTP protocol. - Both the popular Model Context Protocol (MCP) and Agent2Agent (A2A) protocols are based to some extent on SSE - - https://www.anthropic.com/news/model-context-protocol - - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ + - https://www.anthropic.com/news/model-context-protocol + - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ ## Server Implementation + ### Return data + ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" @@ -31,52 +35,55 @@ func HandleSSE(ctx context.Context, c *app.RequestContext) { w.WriteEvent("id-x", "message", []byte("hello\n\nworld")) time.Sleep(10 * time.Millisecond) } - w.Close() // 发送最后的chunk数据,确保优雅退出。可选,Hertz 在 Handler 返回后会自动调用。 + w.Close() // Send the last chunk of data to ensure graceful exit. Optional, Hertz will automatically call this after the Handler returns. - // 请确保 writer 的生命周期和handler一致。不要go异步后台使用。 + // Please make sure that the lifecycle of the writer is consistent with that of the handler. Do not use it in asynchronous background. } ``` - ### FAQ + #### connection has been closed when flush -Reason: The client connection has been disconnected, possibly due to a long time without response data. Please check the corresponding configuration. +Reason: The client connection has been disconnected, possibly due to a long time without response data. Please check the corresponding configuration. ## Client Implementation ### Initiate a request + It's exactly the same as making a normal HTTP request to Hertz. Note: The new Hertz version will automatically recognize SSE streams, so you don't need to explicitly set `WithResponseBodyStream(true)` **Some optional headers** + ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" -sse.AddAcceptMIME(req) // 部分SSE Server可能会要求显式增加 Accept: text/event-stream -sse.SetLastEventID(req, "id-123") // 对于有状态服务,需要通过 SetLastEventID 告诉 Server +sse.AddAcceptMIME(req) // Some SSE Servers may require explicit addition of Accept: text/event-stream +sse.SetLastEventID(req, "id-123") // For stateful services, you need to tell the Server via SetLastEventID ``` ### Handle response + ```go 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 + return err } - - // 也可以手动调用 r.Read 方法 + + // You can also call the r.Read method manually err = r.ForEach(ctx, func(e *Event) error { println("Event:", e.String()) return nil }) - if err != nil { // 如果 Server 正常断开,这里 err == nil,不会报错 - // 其他io错误 或 ctx cancelled + if err != nil { // If the Server is disconnected normally, err == nil here, no error will be reported + // Other io errors or ctx cancelled return err } - println("Client LastEventID", r.LastEventID()) // 可用于保存最后接收的 Event ID + println("Client LastEventID", r.LastEventID()) // Can be used to save the last received Event ID return nil } ``` @@ -84,20 +91,22 @@ func HandleSSE(ctx context.Context, resp *protocol.Response) error { ### FAQ #### How to implement multi-level SSE/SSE Proxy? + - You can refer to the implementations on both the Client and Server sides simultaneously - Enable `WithSenseClientDisconnection` in the server and pass the context to the next hop's Reader ForEach - - In this way, when the Client disconnects, it will be automatically recognized, and the entire stream will be interrupted + - In this way, when the Client disconnects, it will be automatically recognized, and the entire stream will be interrupted ## Event Struct + ```go // Event represents a Server-Sent Event (SSE). type Event struct { - ID string // 即 Event ID,sse.Reader 会自动记录最后的 Event ID,可使用 LastEventID() 获取 - Type string // 即 Event Type,常见的如 "message" - Data []byte // 为了方便使用 Unmarshal/Marshal,这里使用 []byte,但是按spec这个字段必须要 utf8 string + ID string // Event ID, sse.Reader will automatically record the last Event ID, which can be obtained using LastEventID() + Type string // Event Type, such as "message" + Data []byte // []byte is used here for the convenience of Unmarshal/Marshal, but according to spec, this field must be utf8 string - // 不建议使用,主要是针对浏览器的 SourceEvent 返回该字段控制其重试策略。 - // 如果使用Hertz作为Client可以参考:https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ + // Not recommended, mainly for the browser's SourceEvent to return this field to control its retry strategy. + // If you use Hertz as a client, you can refer to: https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ Retry time.Duration } ``` diff --git a/content/zh/docs/hertz/tutorials/basic-feature/_index.md b/content/zh/docs/hertz/tutorials/basic-feature/_index.md index 116f91c8a73..53a3fe18508 100644 --- a/content/zh/docs/hertz/tutorials/basic-feature/_index.md +++ b/content/zh/docs/hertz/tutorials/basic-feature/_index.md @@ -23,6 +23,8 @@ keywords: "常量", "渲染", "JSON Marshal 库", + "SSE", + "http.Handler adaptor", ] description: "Hertz 基本特性。" --- diff --git a/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md b/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md index b3721edb12f..f49cb690c68 100644 --- a/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md +++ b/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md @@ -1,22 +1,25 @@ --- -title: "Hertz http.Handler adaptor" +title: "http.Handler adaptor" date: 2025-05-21 -weight: 12 +weight: 20 keywords: ["adaptor"] -description: "Hertz http.Handler adaptor" +description: "" --- ## 背景 -- 主淘汰长期没有良好维护的 hertz 原生 fs 实现; + +- 淘汰长期没有良好维护的 hertz 原生 fs 实现; - 通过官方 net/http 生态提供一个兼容性较好,性能也不差的 fs 实现。 - 通过官方 net/http 生态扩展 hertz 功能,减少大量扩展实现 ## 什么是 adaptor.HertzHandler + - 允许你将现有的 http.HandlerFunc 方法直接转为 HertzHandler - 可以直接使用 http.FileServer embed.FS 等官方生态 - 甚至可以直接在 Hertz 使用 github.com/gorilla/websocket ## 具体例子 + ```go package main diff --git a/content/zh/docs/hertz/tutorials/basic-feature/sse.md b/content/zh/docs/hertz/tutorials/basic-feature/sse.md index e163cd7e24d..e98b18872f5 100644 --- a/content/zh/docs/hertz/tutorials/basic-feature/sse.md +++ b/content/zh/docs/hertz/tutorials/basic-feature/sse.md @@ -1,26 +1,30 @@ --- title: "SSE" date: 2025-05-21 -weight: 12 +weight: 19 keywords: ["SSE"] -description: "Hertz 提供的 SSE 能力。" +description: "Hertz 提供的 SSE 能力" --- ```代码:https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` **Changelog** + - 2025/05/15 已发布到开源版本 v0.10.0 - 2025/05/13 [d3c68463] 增加 Get / SetLastEventID - 2025/05/13 [3952e956] Reader ForEach 接口支持传入 ctx - 2025/04/24 [cf38573ce] 第一版本完成 -## 什么是Server-Sent Events (SSE) -- SSE是一个HTML标准,用于描述浏览器 EventSource API 的实现。严格说,不是http协议。 +## 什么是 Server-Sent Events (SSE) + +- SSE 是一个 HTML 标准,用于描述浏览器 EventSource API 的实现。严格说,不是 http 协议。 - 流行的 Model Context Protocol (MCP) 或 Agent2Agent (A2A) 协议都一定程度基于 SSE - - https://www.anthropic.com/news/model-context-protocol - - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ + - https://www.anthropic.com/news/model-context-protocol + - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ + +## Server 端实现 -## Server端实现 ### 返回数据 + ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" @@ -31,34 +35,37 @@ func HandleSSE(ctx context.Context, c *app.RequestContext) { w.WriteEvent("id-x", "message", []byte("hello\n\nworld")) time.Sleep(10 * time.Millisecond) } - w.Close() // 发送最后的chunk数据,确保优雅退出。可选,Hertz 在 Handler 返回后会自动调用。 + w.Close() // 发送最后的 chunk 数据,确保优雅退出。可选,Hertz 在 Handler 返回后会自动调用。 - // 请确保 writer 的生命周期和handler一致。不要go异步后台使用。 + // 请确保 writer 的生命周期和 handler 一致。不要 go 异步后台使用。 } ``` - ### 常见问题 + #### connection has been closed when flush -原因: -Client连接断开,可能是太久没有响应数据,请检查相应配置。 +原因: +Client 连接断开,可能是太久没有响应数据,请检查相应配置。 -## Client端实现 +## Client 端实现 ### 发起请求 -与正常 Hertz 发起一个普通的http请求完全一致。 -注意:新hertz版本会自动识别SSE流,不用显式设置 WithResponseBodyStream(true) + +与正常 Hertz 发起一个普通的 http 请求完全一致。 +注意:新 hertz 版本会自动识别 SSE 流,不用显式设置 WithResponseBodyStream(true) **部分可选 Header** + ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" -sse.AddAcceptMIME(req) // 部分SSE Server可能会要求显式增加 Accept: text/event-stream +sse.AddAcceptMIME(req) // 部分 SSE Server 可能会要求显式增加 Accept: text/event-stream sse.SetLastEventID(req, "id-123") // 对于有状态服务,需要通过 SetLastEventID 告诉 Server ``` ### 处理返回 + ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" @@ -74,7 +81,7 @@ func HandleSSE(ctx context.Context, resp *protocol.Response) error { return nil }) if err != nil { // 如果 Server 正常断开,这里 err == nil,不会报错 - // 其他io错误 或 ctx cancelled + // 其他 io 错误 或 ctx cancelled return err } println("Client LastEventID", r.LastEventID()) // 可用于保存最后接收的 Event ID @@ -85,20 +92,22 @@ func HandleSSE(ctx context.Context, resp *protocol.Response) error { ### 常见问题 #### 如何实现多级的 SSE / SSE Proxy? -- 可以同时参考 Client 端和 Server端的实现 + +- 可以同时参考 Client 端和 Server 端的实现 - Server 启用 `WithSenseClientDisconnection` 并且把 context 传入 下一跳的 Reader ForEach - - 这样当Client断开时,会自动识别,并且中断整个流 + - 这样当 Client 断开时,会自动识别,并且中断整个流 ## Event 结构体 + ```go // Event represents a Server-Sent Event (SSE). type Event struct { ID string // 即 Event ID,sse.Reader 会自动记录最后的 Event ID,可使用 LastEventID() 获取 Type string // 即 Event Type,常见的如 "message" - Data []byte // 为了方便使用 Unmarshal/Marshal,这里使用 []byte,但是按spec这个字段必须要 utf8 string + Data []byte // 为了方便使用 Unmarshal/Marshal,这里使用 []byte,但是按 spec 这个字段必须要 utf8 string // 不建议使用,主要是针对浏览器的 SourceEvent 返回该字段控制其重试策略。 - // 如果使用Hertz作为Client可以参考:https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ + // 如果使用 Hertz 作为 Client 可以参考:https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ Retry time.Duration } ``` From 5523a074d2c99a2ca0f4a4d35661d6a927ac7cb5 Mon Sep 17 00:00:00 2001 From: Kyle Xiao Date: Thu, 22 May 2025 11:29:31 +0800 Subject: [PATCH 3/4] chore: refine translations --- .../tutorials/basic-feature/http-adaptor.md | 12 +-- .../docs/hertz/tutorials/basic-feature/sse.md | 76 +++++++++---------- .../tutorials/basic-feature/http-adaptor.md | 4 +- .../docs/hertz/tutorials/basic-feature/sse.md | 7 -- 4 files changed, 43 insertions(+), 56 deletions(-) diff --git a/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md b/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md index 16a8a799901..382439e1526 100644 --- a/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md +++ b/content/en/docs/hertz/tutorials/basic-feature/http-adaptor.md @@ -8,15 +8,15 @@ description: "" ## Background -- We will deprecate the Hertz's native file system -- Provide an fs implementation that is both compatible and performs well, using the official net/http ecosystem. -- Reduce the implementation of a large number of extensions by expanding the hertz function through the official net/http ecosystem +- Phasing out the poorly maintained hertz app.FS implementation +- Providing a compatible and performant fs implementation through the official net/http ecosystem +- Extending hertz functionality through the official net/http ecosystem to reduce custom implementations ## What is adaptor.HertzHandler -- You are allowed to directly convert your existing http.HandlerFunc method into a HertzHandler. -- You can directly use http.FileServer, embed.FS, and other official ecosystems -- You can even use this directly at Hertz github.com/gorilla/websocket +- Allows you to convert existing http.HandlerFunc methods directly to HertzHandler +- Enables direct use of standard library methods like http.FileServer and embed.FS +- Even allows direct use of github.com/gorilla/websocket in Hertz ## Example diff --git a/content/en/docs/hertz/tutorials/basic-feature/sse.md b/content/en/docs/hertz/tutorials/basic-feature/sse.md index b48197d3d6a..d7cc85a278c 100644 --- a/content/en/docs/hertz/tutorials/basic-feature/sse.md +++ b/content/en/docs/hertz/tutorials/basic-feature/sse.md @@ -3,27 +3,20 @@ title: "SSE" date: 2025-05-21 weight: 19 keywords: ["SSE"] -description: "SSE in Hertz" +description: "SSE capabilities provided by Hertz" --- -```Code:https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` - -**Changelog** - -- 2025/05/15 Has been released to the open-source version v0.10.0 -- 2025/05/13 [d3c68463] Added Get / SetLastEventID. -- 2025/05/13 [3952e956] The Reader ForEach interface supports passing in ctx. -- 2025/04/24 [cf38573ce] First version completed +```code: https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` ## What is Server-Sent Events (SSE) -- SSE is an HTML standard used to describe the implementation of the EventSource API in browsers. Strictly speaking, it is not an HTTP protocol. -- Both the popular Model Context Protocol (MCP) and Agent2Agent (A2A) protocols are based to some extent on SSE +- SSE is an HTML standard used to describe the implementation of browser EventSource API. Strictly speaking, it's not an HTTP protocol. +- Popular protocols like Model Context Protocol (MCP) or Agent2Agent (A2A) are based on SSE to some extent - https://www.anthropic.com/news/model-context-protocol - https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/ -## Server Implementation +## Server-side Implementation -### Return data +### Returning Data ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" @@ -35,35 +28,36 @@ func HandleSSE(ctx context.Context, c *app.RequestContext) { w.WriteEvent("id-x", "message", []byte("hello\n\nworld")) time.Sleep(10 * time.Millisecond) } - w.Close() // Send the last chunk of data to ensure graceful exit. Optional, Hertz will automatically call this after the Handler returns. + w.Close() // Send the last chunk of data to ensure graceful exit. Optional, Hertz will automatically call it after the Handler returns. - // Please make sure that the lifecycle of the writer is consistent with that of the handler. Do not use it in asynchronous background. + // Please ensure the writer's lifecycle is consistent with the handler. Don't use it asynchronously in the background. } ``` -### FAQ +### Common Issues #### connection has been closed when flush -Reason: The client connection has been disconnected, possibly due to a long time without response data. Please check the corresponding configuration. +Reason: +Client connection is disconnected, possibly due to no response data for too long. Please check the relevant configuration. -## Client Implementation +## Client-side Implementation -### Initiate a request +### Initiating a Request -It's exactly the same as making a normal HTTP request to Hertz. -Note: The new Hertz version will automatically recognize SSE streams, so you don't need to explicitly set `WithResponseBodyStream(true)` +It's exactly the same as initiating a regular HTTP request with Hertz. +Note: New Hertz versions will automatically identify SSE streams, so you don't need to explicitly set WithResponseBodyStream(true) -**Some optional headers** +**Some Optional Headers** ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" -sse.AddAcceptMIME(req) // Some SSE Servers may require explicit addition of Accept: text/event-stream -sse.SetLastEventID(req, "id-123") // For stateful services, you need to tell the Server via SetLastEventID +sse.AddAcceptMIME(req) // Some SSE Servers may require explicitly adding Accept: text/event-stream +sse.SetLastEventID(req, "id-123") // For stateful services, you need to tell the Server through SetLastEventID ``` -### Handle response +### Handling Responses ```go import "github.com/cloudwego/hertz/pkg/protocol/sse" @@ -71,16 +65,16 @@ 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 + return err } - - // You can also call the r.Read method manually + + // You can also manually call the r.Read method err = r.ForEach(ctx, func(e *Event) error { println("Event:", e.String()) return nil }) - if err != nil { // If the Server is disconnected normally, err == nil here, no error will be reported - // Other io errors or ctx cancelled + if err != nil { // If the Server disconnects normally, err == nil, no error will be reported + // Other IO errors or ctx cancelled return err } println("Client LastEventID", r.LastEventID()) // Can be used to save the last received Event ID @@ -88,25 +82,25 @@ func HandleSSE(ctx context.Context, resp *protocol.Response) error { } ``` -### FAQ +### Common Issues -#### How to implement multi-level SSE/SSE Proxy? +#### How to implement multi-level SSE / SSE Proxy? -- You can refer to the implementations on both the Client and Server sides simultaneously -- Enable `WithSenseClientDisconnection` in the server and pass the context to the next hop's Reader ForEach - - In this way, when the Client disconnects, it will be automatically recognized, and the entire stream will be interrupted +- You can refer to both Client-side and Server-side implementations +- Enable `WithSenseClientDisconnection` on the Server and pass the context to the next hop's Reader ForEach + - This way, when the Client disconnects, it will be automatically detected and the entire stream will be interrupted -## Event Struct +## Event Structure ```go // Event represents a Server-Sent Event (SSE). type Event struct { - ID string // Event ID, sse.Reader will automatically record the last Event ID, which can be obtained using LastEventID() - Type string // Event Type, such as "message" - Data []byte // []byte is used here for the convenience of Unmarshal/Marshal, but according to spec, this field must be utf8 string + ID string // This is the Event ID, sse.Reader will automatically record the last Event ID, which can be obtained using LastEventID() + Type string // This is the Event Type, common ones like "message" + Data []byte // For convenience with Unmarshal/Marshal, []byte is used here, but according to the spec, this field must be a utf8 string - // Not recommended, mainly for the browser's SourceEvent to return this field to control its retry strategy. - // If you use Hertz as a client, you can refer to: https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ + // Not recommended to use, mainly for controlling the retry strategy of the browser's SourceEvent. + // If using Hertz as a Client, you can refer to: https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/retry/ Retry time.Duration } ``` diff --git a/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md b/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md index f49cb690c68..1f37fd5a6bd 100644 --- a/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md +++ b/content/zh/docs/hertz/tutorials/basic-feature/http-adaptor.md @@ -8,14 +8,14 @@ description: "" ## 背景 -- 淘汰长期没有良好维护的 hertz 原生 fs 实现; +- 淘汰长期没有良好维护的 hertz app.FS 实现 - 通过官方 net/http 生态提供一个兼容性较好,性能也不差的 fs 实现。 - 通过官方 net/http 生态扩展 hertz 功能,减少大量扩展实现 ## 什么是 adaptor.HertzHandler - 允许你将现有的 http.HandlerFunc 方法直接转为 HertzHandler -- 可以直接使用 http.FileServer embed.FS 等官方生态 +- 可以直接使用 http.FileServer embed.FS 等标准库方法 - 甚至可以直接在 Hertz 使用 github.com/gorilla/websocket ## 具体例子 diff --git a/content/zh/docs/hertz/tutorials/basic-feature/sse.md b/content/zh/docs/hertz/tutorials/basic-feature/sse.md index e98b18872f5..bf469d596e3 100644 --- a/content/zh/docs/hertz/tutorials/basic-feature/sse.md +++ b/content/zh/docs/hertz/tutorials/basic-feature/sse.md @@ -7,13 +7,6 @@ description: "Hertz 提供的 SSE 能力" --- ```代码:https://github.com/cloudwego/hertz/tree/develop/pkg/protocol/sse``` -**Changelog** - -- 2025/05/15 已发布到开源版本 v0.10.0 -- 2025/05/13 [d3c68463] 增加 Get / SetLastEventID -- 2025/05/13 [3952e956] Reader ForEach 接口支持传入 ctx -- 2025/04/24 [cf38573ce] 第一版本完成 - ## 什么是 Server-Sent Events (SSE) - SSE 是一个 HTML 标准,用于描述浏览器 EventSource API 的实现。严格说,不是 http 协议。 From b49b1f91ec54b5fcce066eafd31ef26531558afa Mon Sep 17 00:00:00 2001 From: "qiheng.zhou" Date: Thu, 22 May 2025 11:52:03 +0800 Subject: [PATCH 4/4] chore: markdown format --- content/en/docs/hertz/tutorials/basic-feature/sse.md | 1 + content/zh/docs/hertz/tutorials/basic-feature/sse.md | 1 + 2 files changed, 2 insertions(+) diff --git a/content/en/docs/hertz/tutorials/basic-feature/sse.md b/content/en/docs/hertz/tutorials/basic-feature/sse.md index d7cc85a278c..6e5140b4f35 100644 --- a/content/en/docs/hertz/tutorials/basic-feature/sse.md +++ b/content/en/docs/hertz/tutorials/basic-feature/sse.md @@ -46,6 +46,7 @@ Client connection is disconnected, possibly due to no response data for too long ### Initiating a Request It's exactly the same as initiating a regular HTTP request with Hertz. + Note: New Hertz versions will automatically identify SSE streams, so you don't need to explicitly set WithResponseBodyStream(true) **Some Optional Headers** diff --git a/content/zh/docs/hertz/tutorials/basic-feature/sse.md b/content/zh/docs/hertz/tutorials/basic-feature/sse.md index bf469d596e3..94a6b40f12e 100644 --- a/content/zh/docs/hertz/tutorials/basic-feature/sse.md +++ b/content/zh/docs/hertz/tutorials/basic-feature/sse.md @@ -46,6 +46,7 @@ Client 连接断开,可能是太久没有响应数据,请检查相应配置 ### 发起请求 与正常 Hertz 发起一个普通的 http 请求完全一致。 + 注意:新 hertz 版本会自动识别 SSE 流,不用显式设置 WithResponseBodyStream(true) **部分可选 Header**