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)
}

这个示例展示了:

  1. 添加多个不同类型的关闭钩子
  2. 使用自定义key添加和移除钩子
  3. 处理特定的信号
  4. 模拟应用运行和关闭过程

更多关于golang应用关闭钩子处理插件库shutdown的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于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("应用程序已优雅关闭")
}

最佳实践

  1. 资源释放顺序:按照资源依赖关系的逆序释放,通常是先关闭HTTP服务器,然后关闭数据库连接,最后是其他资源。

  2. 超时设置:为每个关闭操作设置合理的超时时间,防止应用程序无法正常退出。

  3. 日志记录:记录关闭过程中的关键事件,便于排查问题。

  4. 错误处理:即使某些关闭操作失败,也应继续执行其他关闭操作。

  5. 测试验证:确保关闭逻辑在各种场景下都能正常工作。

通过使用shutdown库,你可以确保你的Golang应用在收到终止信号时能够优雅地关闭,释放所有资源,并完成必要的清理工作。

回到顶部