golang应用关闭钩子处理插件库shutdown的使用
Golang应用关闭钩子处理插件库shutdown的使用
概述
shutdown
是一个用于处理Golang应用关闭钩子的插件库,提供了方便的接口来操作os.Signal
。它允许添加多个钩子函数,这些钩子会在应用关闭时被同时调用。
安装
import "github.com/ztrue/shutdown"
基本示例
package main
import (
"log"
"time"
"github.com/ztrue/shutdown"
)
func main() {
// 添加关闭钩子
shutdown.Add(func() {
// 可以在这里执行关闭前的清理工作:
// 写入日志、停止文件写入、关闭连接等
log.Println("Stopping...")
log.Println("3")
time.Sleep(time.Second)
log.Println("2")
time.Sleep(time.Second)
log.Println("1")
time.Sleep(time.Second)
log.Println("0, stopped")
})
// 模拟应用运行
go func() {
log.Println("App running, press CTRL + C to stop")
select {}
}()
// 开始监听关闭信号
shutdown.Listen()
}
主要功能
添加关闭钩子
shutdown.Add(func() {
log.Println("Stopping")
})
移除钩子
key := shutdown.Add(func() {
log.Println("Stopping")
})
shutdown.Remove(key)
使用自定义键添加钩子
shutdown.AddWithKey("mykey", func() {
log.Println("Stopping")
})
shutdown.Remove("mykey")
带信号参数的钩子
shutdown.AddWithParam(func(sig os.Signal) {
log.Println("Stopping because of", sig)
})
监听特定信号
shutdown.Listen(syscall.SIGINT, syscall.SIGTERM)
热重载配置
shutdown.AddWithParam(func(sig os.Signal) {
if sig == syscall.SIGHUP {
log.Println("Reload conf...")
reloadConf()
// 重新监听信号以避免关闭
shutdown.Listen(syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
} else { // SIGINT, SIGTERM
log.Println("Stopping...")
}
})
shutdown.Listen(syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
完整示例
下面是一个完整的示例,展示了如何使用shutdown库处理应用关闭:
package main
import (
"log"
"os"
"syscall"
"time"
"github.com/ztrue/shutdown"
)
func main() {
// 添加第一个关闭钩子
shutdown.Add(func() {
log.Println("Cleaning up resources...")
time.Sleep(1 * time.Second)
log.Println("Resources cleaned up")
})
// 添加第二个关闭钩子(带自定义key)
key := shutdown.AddWithKey("logger", func() {
log.Println("Closing log files...")
time.Sleep(1 * time.Second)
log.Println("Log files closed")
})
// 添加带信号参数的钩子
shutdown.AddWithParam(func(sig os.Signal) {
log.Printf("Received signal: %v\n", sig)
})
// 模拟一些工作
go func() {
log.Println("Application started, press CTRL+C to stop")
for i := 0; i < 10; i++ {
log.Printf("Working... %d/10", i+1)
time.Sleep(1 * time.Second)
}
}()
// 5秒后移除logger钩子
go func() {
time.Sleep(5 * time.Second)
shutdown.Remove(key)
log.Println("Logger hook removed")
}()
// 监听SIGINT和SIGTERM信号
shutdown.Listen(syscall.SIGINT, syscall.SIGTERM)
}
这个示例展示了:
- 添加多个不同类型的关闭钩子
- 使用自定义key添加和移除钩子
- 处理特定的信号
- 模拟应用运行和关闭过程
更多关于golang应用关闭钩子处理插件库shutdown的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang应用关闭钩子处理插件库shutdown的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang应用关闭钩子处理:shutdown库使用指南
在Golang应用中,优雅关闭(graceful shutdown)是一个重要特性,特别是在处理HTTP服务、数据库连接等资源时。shutdown
是一个流行的Golang库,用于管理应用程序的关闭钩子。
shutdown库简介
shutdown
库提供了一种简单的方式来注册关闭钩子,这些钩子会在应用收到中断信号(如SIGINT或SIGTERM)时执行。它支持按优先级顺序执行关闭操作,并可以设置超时。
安装
go get github.com/oklog/run
# 或者使用更专门的shutdown库
go get github.com/zbindenren/signal
基本用法
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/zbindenren/signal"
)
func main() {
// 创建一个HTTP服务器
srv := &http.Server{
Addr: ":8080",
}
// 创建shutdown管理器
shutdown := signal.NewHandler()
// 注册关闭钩子
shutdown.OnShutdown(func() {
fmt.Println("执行第一个关闭任务...")
time.Sleep(1 * time.Second)
})
shutdown.OnShutdown(func() {
fmt.Println("执行第二个关闭任务...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("HTTP服务器关闭错误: %v", err)
}
fmt.Println("HTTP服务器已关闭")
})
// 启动HTTP服务器
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("监听错误: %v", err)
}
}()
fmt.Println("服务已启动,按Ctrl+C停止")
// 等待中断信号
shutdown.WaitForInterrupt()
}
高级特性
1. 带优先级的关闭钩子
shutdown := signal.NewHandler()
// 高优先级任务(先执行)
shutdown.OnShutdownWithPriority(100, func() {
fmt.Println("高优先级任务执行")
})
// 默认优先级是0
shutdown.OnShutdown(func() {
fmt.Println("普通优先级任务执行")
})
// 低优先级任务(后执行)
shutdown.OnShutdownWithPriority(-100, func() {
fmt.Println("低优先级任务执行")
})
2. 带上下文的关闭钩子
shutdown.OnShutdownWithContext(func(ctx context.Context) {
select {
case <-time.After(2 * time.Second):
fmt.Println("长时间运行的任务完成")
case <-ctx.Done():
fmt.Println("任务因超时被取消")
}
})
3. 自定义信号处理
shutdown := signal.NewHandler(
signal.WithSignals(syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP),
signal.WithTimeout(30*time.Second),
)
实际应用示例
下面是一个更完整的示例,展示如何在Web服务器中使用shutdown:
package main
import (
"context"
"database/sql"
"fmt"
"log"
"net/http"
"os"
"syscall"
"time"
"github.com/zbindenren/signal"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 初始化数据库连接
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
log.Fatal(err)
}
// 创建HTTP路由器
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
})
// 配置HTTP服务器
srv := &http.Server{
Addr: ":8080",
Handler: mux,
}
// 创建shutdown管理器,设置30秒超时
shutdown := signal.NewHandler(
signal.WithTimeout(30*time.Second),
signal.WithLogger(signal.NewStdLogger()),
)
// 注册关闭钩子 - 关闭HTTP服务器
shutdown.OnShutdown(func() {
log.Println("正在关闭HTTP服务器...")
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("HTTP服务器关闭错误: %v", err)
}
log.Println("HTTP服务器已关闭")
})
// 注册关闭钩子 - 关闭数据库连接
shutdown.OnShutdown(func() {
log.Println("正在关闭数据库连接...")
if err := db.Close(); err != nil {
log.Printf("数据库关闭错误: %v", err)
}
log.Println("数据库连接已关闭")
})
// 启动HTTP服务器
go func() {
log.Printf("服务器监听在 %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("监听错误: %v", err)
}
}()
// 等待中断信号
shutdown.WaitForInterrupt()
log.Println("应用程序已优雅关闭")
}
最佳实践
-
资源释放顺序:按照资源依赖关系的逆序释放,通常是先关闭HTTP服务器,然后关闭数据库连接,最后是其他资源。
-
超时设置:为每个关闭操作设置合理的超时时间,防止应用程序无法正常退出。
-
日志记录:记录关闭过程中的关键事件,便于排查问题。
-
错误处理:即使某些关闭操作失败,也应继续执行其他关闭操作。
-
测试验证:确保关闭逻辑在各种场景下都能正常工作。
通过使用shutdown
库,你可以确保你的Golang应用在收到终止信号时能够优雅地关闭,释放所有资源,并完成必要的清理工作。