golang实现应用程序顺序/并发优雅终止插件库graterm的使用
graterm - Golang 实现应用程序顺序/并发优雅终止插件库
graterm 是一个提供有序优雅终止(Graceful Termination)功能的 Go 语言库,可以帮助应用程序在收到终止信号时按顺序或并发地执行清理操作。
⚡ 功能描述
该库提供流畅的方法来注册有序的应用程序终止钩子(hooks),并阻塞主 goroutine 直到注册的 os.Signal
出现。
具有相同 Order 的终止钩子将并发执行。可以为每个注册的终止钩子设置单独的超时时间,也可以为整个应用程序设置全局终止超时。
🎯 特性
- 仅依赖标准 Go 库(测试除外)
- 与组件无关(可适配任何第三方技术)
- 干净且经过测试的代码:100% 测试覆盖率,包括 goroutine 泄漏测试
- 丰富的示例集
⚙️ 使用方法
安装
go get -u github.com/skovtunenko/graterm
导入
import (
"github.com/skovtunenko/graterm"
)
基础示例
package main
import (
"context"
"log"
"syscall"
"time"
"github.com/skovtunenko/graterm"
)
func main() {
// 定义终止顺序
const (
HTTPServerTerminationOrder graterm.Order = 1
MessagingTerminationOrder graterm.Order = 1
DBTerminationOrder graterm.Order = 2
)
// 创建 Terminator 实例
terminator, appCtx := graterm.NewWithSignals(context.Background(), syscall.SIGINT, syscall.SIGTERM)
terminator.SetLogger(log.Default()) // 可选步骤
// 注册 HTTP Server 终止钩子
terminator.WithOrder(HTTPServerTerminationOrder).
WithName("HTTP Server"). // 设置名称是可选的,只有在提供 logger 实例时才有用
Register(1*time.Second, func(ctx context.Context) {
log.Println("terminating HTTP Server...")
defer log.Println("...HTTP Server terminated")
})
// 注册无名称的 Messaging 终止钩子
terminator.WithOrder(MessagingTerminationOrder).
Register(1*time.Second, func(ctx context.Context) {
log.Println("terminating Messaging...")
defer log.Println("...Messaging terminated")
})
// 注册 Database 终止钩子
terminator.WithOrder(DBTerminationOrder).
WithName("DB"). // 设置名称是可选的,只有在提供 logger 实例时才有用
Register(1*time.Second, func(ctx context.Context) {
log.Println("terminating DB...")
defer log.Println("...DB terminated")
const sleepTime = 3 * time.Second
select {
case <-time.After(sleepTime):
log.Printf("DB termination sleep time %v is over\n", sleepTime)
case <-ctx.Done():
log.Printf("DB termination Context is Done because of: %+v\n", ctx.Err())
}
})
// 等待 os.Signal 出现,然后以最多 20 秒的超时时间终止应用程序
if err := terminator.Wait(appCtx, 20 * time.Second); err != nil {
log.Printf("graceful termination period was timed out: %+v", err)
}
}
与 HTTP 服务器集成示例
package main
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"syscall"
"time"
"github.com/skovtunenko/graterm"
)
func main() {
// 定义 HTTP Server 终止顺序
const HTTPServerTerminationOrder graterm.Order = 1
// 创建 Terminator 实例
terminator, appCtx := graterm.NewWithSignals(context.Background(), syscall.SIGINT, syscall.SIGTERM)
terminator.SetLogger(log.Default()) // 可选步骤
// 创建 HTTP Server 并添加一个简单的处理程序
httpServer := &http.Server{
Addr: ":8080",
Handler: http.DefaultServeMux,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, world!")
})
// 在单独的 goroutine 中启动 HTTP 服务器
go func() {
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Printf("terminated HTTP Server: %+v\n", err)
}
}()
// 注册 HTTP Server 终止钩子
terminator.WithOrder(HTTPServerTerminationOrder).
WithName("HTTPServer"). // 设置名称是可选的,只有在提供 logger 实例时才有用
Register(10*time.Second, func(ctx context.Context) {
if err := httpServer.Shutdown(ctx); err != nil {
log.Printf("shutdown HTTP Server: %+v\n", err)
}
})
// 等待 os.Signal 出现,然后以最多 30 秒的超时时间终止应用程序
if err := terminator.Wait(appCtx, 30*time.Second); err != nil {
log.Printf("graceful termination period is timed out: %+v\n", err)
}
}
测试
运行单元测试:
make test
运行代码质量检查:
make code-quality
许可证
MIT 许可证
作者
- Sergiy Kovtunenko
- Oleksandr Halushchak
graterm 提供了一种简单而强大的方式来实现 Go 应用程序的优雅终止功能,确保资源能够按正确的顺序释放,同时提供灵活的配置选项来满足不同场景的需求。
更多关于golang实现应用程序顺序/并发优雅终止插件库graterm的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现应用程序顺序/并发优雅终止插件库graterm的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 实现应用程序优雅终止 - graterm 使用指南
在 Go 语言中实现应用程序的优雅终止是一个重要课题,特别是在微服务架构中。graterm
是一个专门用于管理应用程序优雅终止的 Go 库,它支持顺序和并发两种终止方式。
graterm 简介
graterm
是一个轻量级库,主要功能包括:
- 注册多个需要在程序退出时执行的终止函数
- 支持顺序执行或并发执行终止函数
- 支持超时控制
- 与标准库的
context
和os/signal
良好集成
安装
go get github.com/superhawk610/graterm
基本使用示例
1. 顺序终止示例
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/superhawk610/graterm"
)
func main() {
// 创建 Terminator 实例
term := graterm.NewTerminator()
// 注册需要优雅终止的资源
term.Register(graterm.TermFunc(func(ctx context.Context) error {
fmt.Println("关闭数据库连接...")
time.Sleep(1 * time.Second) // 模拟关闭操作
fmt.Println("数据库连接已关闭")
return nil
}))
term.Register(graterm.TermFunc(func(ctx context.Context) error {
fmt.Println("关闭HTTP服务器...")
time.Sleep(1 * time.Second) // 模拟关闭操作
fmt.Println("HTTP服务器已关闭")
return nil
}))
// 监听系统信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 模拟应用程序工作
go func() {
for i := 0; i < 10; i++ {
fmt.Printf("Working... %d\n", i)
time.Sleep(500 * time.Millisecond)
}
}()
// 等待信号
sig := <-sigChan
fmt.Printf("接收到信号: %v\n", sig)
// 执行优雅终止(顺序执行)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := term.TerminateSequential(ctx); err != nil {
fmt.Printf("终止时出错: %v\n", err)
os.Exit(1)
}
fmt.Println("应用程序已优雅终止")
}
2. 并发终止示例
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/superhawk610/graterm"
)
func main() {
term := graterm.NewTerminator()
// 注册多个需要关闭的资源
term.Register(graterm.TermFunc(func(ctx context.Context) error {
fmt.Println("关闭Redis连接...")
time.Sleep(2 * time.Second) // 模拟耗时操作
fmt.Println("Redis连接已关闭")
return nil
}))
term.Register(graterm.TermFunc(func(ctx context.Context) error {
fmt.Println("关闭消息队列...")
time.Sleep(1 * time.Second) // 模拟耗时操作
fmt.Println("消息队列已关闭")
return nil
}))
// 监听信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 模拟工作
go func() {
for {
fmt.Println("服务运行中...")
time.Sleep(1 * time.Second)
}
}()
// 等待信号
sig := <-sigChan
fmt.Printf("接收到信号: %v\n", sig)
// 并发执行终止函数
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := term.TerminateConcurrent(ctx); err != nil {
fmt.Printf("终止时出错: %v\n", err)
os.Exit(1)
}
fmt.Println("所有资源已并发关闭")
}
高级用法
带优先级的终止
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/superhawk610/graterm"
)
func main() {
term := graterm.NewTerminator()
// 高优先级任务先注册
term.Register(graterm.TermFunc(func(ctx context.Context) error {
fmt.Println("[高优先级] 保存缓存数据...")
time.Sleep(1 * time.Second)
fmt.Println("[高优先级] 缓存数据已保存")
return nil
}), graterm.WithPriority(100)) // 优先级越高越先执行
// 低优先级任务后注册
term.Register(graterm.TermFunc(func(ctx context.Context) error {
fmt.Println("[低优先级] 关闭日志文件...")
time.Sleep(1 * time.Second)
fmt.Println("[低优先级] 日志文件已关闭")
return nil
}), graterm.WithPriority(10))
// 监听信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 等待信号
sig := <-sigChan
fmt.Printf("接收到信号: %v\n", sig)
// 执行终止
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := term.TerminateSequential(ctx); err != nil {
fmt.Printf("终止时出错: %v\n", err)
os.Exit(1)
}
fmt.Println("应用程序已按优先级顺序终止")
}
最佳实践建议
-
合理设置超时时间:根据实际业务场景设置适当的超时时间,避免程序长时间无法退出。
-
区分关键和非关键资源:关键资源(如数据持久化)应该优先关闭,非关键资源可以放在后面。
-
处理终止错误:即使某些资源关闭失败,也应尽可能继续关闭其他资源,最后再报告错误。
-
并发与顺序的选择:
- 顺序执行适合有依赖关系的资源
- 并发执行适合相互独立的资源,可以加快关闭速度
-
结合 context 使用:确保所有终止函数都能响应 context 取消,避免无法中断的阻塞操作。
graterm
是一个简单但功能完善的优雅终止管理库,通过合理使用可以让你的 Go 应用程序更加健壮和可靠。