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

1 回复

更多关于golang实现应用程序顺序/并发优雅终止插件库graterm的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 实现应用程序优雅终止 - graterm 使用指南

在 Go 语言中实现应用程序的优雅终止是一个重要课题,特别是在微服务架构中。graterm 是一个专门用于管理应用程序优雅终止的 Go 库,它支持顺序和并发两种终止方式。

graterm 简介

graterm 是一个轻量级库,主要功能包括:

  • 注册多个需要在程序退出时执行的终止函数
  • 支持顺序执行或并发执行终止函数
  • 支持超时控制
  • 与标准库的 contextos/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("应用程序已按优先级顺序终止")
}

最佳实践建议

  1. 合理设置超时时间:根据实际业务场景设置适当的超时时间,避免程序长时间无法退出。

  2. 区分关键和非关键资源:关键资源(如数据持久化)应该优先关闭,非关键资源可以放在后面。

  3. 处理终止错误:即使某些资源关闭失败,也应尽可能继续关闭其他资源,最后再报告错误。

  4. 并发与顺序的选择

    • 顺序执行适合有依赖关系的资源
    • 并发执行适合相互独立的资源,可以加快关闭速度
  5. 结合 context 使用:确保所有终止函数都能响应 context 取消,避免无法中断的阻塞操作。

graterm 是一个简单但功能完善的优雅终止管理库,通过合理使用可以让你的 Go 应用程序更加健壮和可靠。

回到顶部