Skip to content

Commit cdc2d6d

Browse files
committed
docs: revamp Chinese docs with best practices and expanded feature usage
- Overhaul the documentation for both simplified and traditional Chinese, adding detailed instructions and best practices for graceful shutdown. - Expand feature list to highlight timeout protection, error reporting, concurrency safety, and zero dependencies. - Add in-depth usage sections including basic usage, timeout configuration, error handling, and option tables. - Enhance example listings with descriptions of each scenario. - Add a "Best Practices" section covering proper shutdown, context handling, idempotency, timeout setup, error checks, and sequential shutdown. Signed-off-by: appleboy <appleboy.tw@gmail.com>
1 parent 68f55e3 commit cdc2d6d

File tree

2 files changed

+534
-18
lines changed

2 files changed

+534
-18
lines changed

README.zh-cn.md

Lines changed: 267 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,37 @@
1818
- [功能特色](#功能特色)
1919
- [安装方式](#安装方式)
2020
- [使用说明](#使用说明)
21+
- [基本使用](#基本使用)
2122
- [新增运行中任务](#新增运行中任务)
2223
- [新增关闭清理任务](#新增关闭清理任务)
24+
- [设置关闭超时](#设置关闭超时)
25+
- [错误处理](#错误处理)
2326
- [自定义 Logger](#自定义-logger)
27+
- [配置选项](#配置选项)
2428
- [示例](#示例)
29+
- [最佳实践](#最佳实践)
30+
- [1. 务必等待 Done()](#1-务必等待-done)
31+
- [2. 响应 Context 取消](#2-响应-context-取消)
32+
- [3. 让关闭任务具有幂等性](#3-让关闭任务具有幂等性)
33+
- [4. 设置适当的超时](#4-设置适当的超时)
34+
- [5. 关闭后检查错误](#5-关闭后检查错误)
35+
- [6. 多个任务的关闭顺序](#6-多个任务的关闭顺序)
2536
- [许可证](#许可证)
2637

2738
---
2839

2940
## 功能特色
3041

31-
- 支持 Go 服务的优雅关闭(graceful shutdown)
32-
- 管理多个运行中任务,并可通过 context 取消
33-
- 注册关闭时的清理 hook
34-
- 支持自定义 logger
35-
- API 简单,易于集成
42+
- **优雅关闭** - 自动处理系统信号(SIGINT、SIGTERM)的 Go 服务
43+
- **超时保护** - 可配置超时时间防止无限期等待(默认:30 秒)
44+
- **重复关闭保护** - 确保关闭逻辑只执行一次,即使收到多个信号
45+
- **基于 Context 的取消** - 运行中任务会收到 context 取消信号
46+
- **并行关闭 Hook** - 清理任务并行执行以加快关闭速度
47+
- **错误报告** - 收集并报告所有任务的错误
48+
- **自定义 Logger 支持** - 整合现有的日志解决方案
49+
- **线程安全** - 所有操作都是并发安全的
50+
- **零依赖** - 轻量且精简
51+
- **简单的 API** - 易于集成到现有服务
3652

3753
---
3854

@@ -46,6 +62,42 @@ go get github.com/appleboy/graceful
4662

4763
## 使用说明
4864

65+
### 基本使用
66+
67+
创建 manager 并等待优雅关闭:
68+
69+
```go
70+
package main
71+
72+
import (
73+
"context"
74+
"log"
75+
"time"
76+
77+
"github.com/appleboy/graceful"
78+
)
79+
80+
func main() {
81+
// 使用默认设置创建 manager
82+
m := graceful.NewManager()
83+
84+
// 添加你的任务...
85+
86+
// 等待关闭完成(会阻塞直到收到 SIGINT/SIGTERM)
87+
<-m.Done()
88+
89+
// 检查错误
90+
if errs := m.Errors(); len(errs) > 0 {
91+
log.Printf("关闭过程中发生 %d 个错误", len(errs))
92+
for _, err := range errs {
93+
log.Printf(" - %v", err)
94+
}
95+
}
96+
97+
log.Println("服务已优雅地停止")
98+
}
99+
```
100+
49101
### 新增运行中任务
50102

51103
注册长时间运行的任务,服务关闭时会自动取消:
@@ -132,6 +184,93 @@ func main() {
132184
}
133185
```
134186

187+
### 设置关闭超时
188+
189+
设置等待优雅关闭的最长时间(默认:30 秒):
190+
191+
```go
192+
package main
193+
194+
import (
195+
"time"
196+
"github.com/appleboy/graceful"
197+
)
198+
199+
func main() {
200+
// 设置 10 秒超时
201+
m := graceful.NewManager(
202+
graceful.WithShutdownTimeout(10 * time.Second),
203+
)
204+
205+
// 或禁用超时(无限期等待)
206+
m := graceful.NewManager(
207+
graceful.WithShutdownTimeout(0),
208+
)
209+
210+
// ... 添加任务 ...
211+
212+
<-m.Done()
213+
214+
// 检查是否发生超时
215+
if errs := m.Errors(); len(errs) > 0 {
216+
for _, err := range errs {
217+
if err.Error() == "shutdown timeout exceeded: 10s" {
218+
log.Println("部分任务未在超时内完成")
219+
}
220+
}
221+
}
222+
}
223+
```
224+
225+
**为什么超时很重要:**
226+
227+
- 防止任务未响应取消信号时无限期挂起
228+
- 对容器化环境(Kubernetes terminationGracePeriodSeconds)至关重要
229+
- 确保生产环境中可预测的关闭行为
230+
231+
### 错误处理
232+
233+
访问关闭期间发生的所有错误:
234+
235+
```go
236+
package main
237+
238+
import (
239+
"log"
240+
"github.com/appleboy/graceful"
241+
)
242+
243+
func main() {
244+
m := graceful.NewManager()
245+
246+
m.AddRunningJob(func(ctx context.Context) error {
247+
// ... 执行任务 ...
248+
return fmt.Errorf("发生错误") // 错误会被收集
249+
})
250+
251+
m.AddShutdownJob(func() error {
252+
// ... 清理 ...
253+
return nil
254+
})
255+
256+
<-m.Done()
257+
258+
// 获取所有错误(包含任务错误、panic 和超时错误)
259+
errs := m.Errors()
260+
if len(errs) > 0 {
261+
log.Printf("关闭错误:%v", errs)
262+
os.Exit(1) // 以错误代码退出
263+
}
264+
}
265+
```
266+
267+
**收集的错误类型:**
268+
269+
- 运行中任务返回的错误
270+
- 关闭任务返回的错误
271+
- 从任务中恢复的 panic(转换为错误)
272+
- 如果关闭超过设置时间的超时错误
273+
135274
### 自定义 Logger
136275

137276
你可以使用自定义 logger(参考 [zerolog 示例](./_example/example03/logger.go)):
@@ -144,12 +283,131 @@ m := graceful.NewManager(
144283

145284
---
146285

286+
## 配置选项
287+
288+
所有配置都通过传递给 `NewManager()` 的功能选项完成:
289+
290+
| 选项 | 说明 | 默认值 |
291+
| ------------------------------- | ------------------------------------------------------- | ---------------------- |
292+
| `WithContext(ctx)` | 使用自定义的父 context。当 context 被取消时会触发关闭。 | `context.Background()` |
293+
| `WithLogger(logger)` | 使用自定义的 logger 实现。 | 内置 logger |
294+
| `WithShutdownTimeout(duration)` | 等待优雅关闭的最长时间。设为 `0` 表示无超时。 | `30 * time.Second` |
295+
296+
**多个选项的示例:**
297+
298+
```go
299+
m := graceful.NewManager(
300+
graceful.WithContext(ctx),
301+
graceful.WithShutdownTimeout(15 * time.Second),
302+
graceful.WithLogger(customLogger),
303+
)
304+
```
305+
306+
---
307+
147308
## 示例
148309

149-
- [基本用法](./_example/example01/main.go)
150-
- [多个任务](./_example/example02/main.go)
151-
- [自定义 logger](./_example/example03/main.go)
152-
- [Gin 集成](./_example/example04-gin/main.go)
310+
- [**示例 01**:基本用法](./_example/example01/main.go) - 简单的运行中任务
311+
- [**示例 02**:多个任务](./_example/example02/main.go) - 运行中 + 关闭任务
312+
- [**示例 03**:自定义 logger](./_example/example03/main.go) - 与 zerolog 集成
313+
- [**示例 04**:Gin 网页服务器](./_example/example04-gin/main.go) - 优雅的 HTTP 服务器关闭
314+
- [**示例 05**:关闭超时](./_example/example05-timeout/main.go) - 超时设置与处理
315+
316+
---
317+
318+
## 最佳实践
319+
320+
### 1. 务必等待 Done()
321+
322+
```go
323+
m := graceful.NewManager()
324+
// ... 添加任务 ...
325+
<-m.Done() // ✅ 必要:等待关闭完成
326+
```
327+
328+
**为什么:** 如果程序在调用 `<-m.Done()` 前就退出,清理可能无法完成,导致:
329+
330+
- 资源泄漏(打开的连接、文件)
331+
- 数据丢失(未刷新的缓冲区)
332+
- 孤儿 goroutine
333+
334+
### 2. 响应 Context 取消
335+
336+
```go
337+
m.AddRunningJob(func(ctx context.Context) error {
338+
ticker := time.NewTicker(1 * time.Second)
339+
defer ticker.Stop()
340+
341+
for {
342+
select {
343+
case <-ctx.Done():
344+
// ✅ 务必处理 ctx.Done() 以启用优雅关闭
345+
log.Println("正在优雅地关闭...")
346+
return ctx.Err()
347+
case <-ticker.C:
348+
// 执行任务
349+
}
350+
}
351+
})
352+
```
353+
354+
**为什么:** 不尊重 `ctx.Done()` 的任务会阻塞关闭直到超时。
355+
356+
### 3. 让关闭任务具有幂等性
357+
358+
```go
359+
m.AddShutdownJob(func() error {
360+
// ✅ 多次调用也安全(虽然 graceful 确保只会调用一次)
361+
if db != nil {
362+
db.Close()
363+
db = nil
364+
}
365+
return nil
366+
})
367+
```
368+
369+
**为什么:** 虽然 manager 确保关闭任务只执行一次,防御性编程可防止问题。
370+
371+
### 4. 设置适当的超时
372+
373+
```go
374+
// 对于 terminationGracePeriodSeconds: 30 的 Kubernetes Pod
375+
m := graceful.NewManager(
376+
graceful.WithShutdownTimeout(25 * time.Second), // ✅ 留 5 秒缓冲给 SIGKILL
377+
)
378+
```
379+
380+
**为什么:** 如果关闭超时超过容器终止期限,进程会被强制终止(SIGKILL)。
381+
382+
### 5. 关闭后检查错误
383+
384+
```go
385+
<-m.Done()
386+
387+
if errs := m.Errors(); len(errs) > 0 {
388+
log.Printf("关闭错误:%v", errs)
389+
os.Exit(1) // ✅ 以错误代码退出以便监控/告警
390+
}
391+
```
392+
393+
**为什么:** 让你能在生产环境中检测并响应关闭问题。
394+
395+
### 6. 多个任务的关闭顺序
396+
397+
关闭任务默认**并行执行**。如果你需要顺序关闭:
398+
399+
```go
400+
m.AddShutdownJob(func() error {
401+
// 在单一任务内依序执行所有关闭步骤
402+
stopAcceptingRequests()
403+
waitForInflightRequests()
404+
closeDatabase()
405+
flushLogs()
406+
return nil
407+
})
408+
```
409+
410+
**为什么:** 并行执行速度较快,但某些清理需要特定顺序。
153411

154412
---
155413

0 commit comments

Comments
 (0)