当前位置: 首页 > news >正文

Go项目中关于优雅关闭的那些事

优雅关闭,是使Go Web项目变得可靠的重要一环。

目录

为什么需要优雅关闭

小demo

信号:

官方源码:


为什么需要优雅关闭

想象一下这个场景:你的服务器正在处理一些用户的请求,这时你需要重启或关闭服务(例如发布新版本)。你直接用 Ctrl+C 或者 kill -9 很粗暴的终止进程,会发生什么?

  • 正在处理的请求会被突然中断,用户可能看到错误。

  • 数据库操作可能只完成一半,导致数据不一致

  • 一些重要的清理工作(如关闭文件、释放连接)无法进行。

而优雅关闭,就是为了解决这个问题。

如:停止接收新的请求、完成所有正在进行的请求、释放资源、最后安全退出什么的....

小demo

package mainimport ("context""log""net/http""os""os/signal""syscall""time""github.com/gin-gonic/gin"
)func main() {// 1. 创建Gin路由引擎router := gin.Default()// 2. 定义一个测试路由,我们让它延迟5秒响应,模拟一个长时间运行的请求router.GET("/", func(c *gin.Context) {time.Sleep(5 * time.Second) // 模拟耗时操作c.JSON(http.StatusOK, gin.H{"message": "请求处理完成!",})})// 3. 创建HTTP Server,并注入Gin引擎srv := &http.Server{Addr:    ":8080",Handler: router,}// 4. 在一个新的goroutine里启动Servergo func() {log.Println("服务器正在启动,监听地址: http://localhost:8080")if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("服务器启动失败:%s\n", err)}}()// 5. 等待中断信号以优雅地关闭服务器(设置 10 秒的超时时间)// 创建一个接收信号的通道quit := make(chan os.Signal, 1)// signal.Notify会监听指定的系统信号,并发送到quit通道// kill 默认会发送 syscall.SIGTERM 信号// kill -2 发送 syscall.SIGINT 信号(我们常用的Ctrl+C)// kill -9 发送 syscall.SIGKILL 信号,但无法捕获,所以不需要添加signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit // 阻塞在这里,直到有信号写入quit通道log.Println("收到关闭信号,正在优雅地关闭服务器...")// 6. 创建一个带有超时的context,用于通知服务器还有10秒时间完成当前请求ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel() // 在函数返回前执行,释放资源// 7. 调用Shutdown方法,传入超时上下文if err := srv.Shutdown(ctx); err != nil {log.Fatal("服务器强制关闭:", err)}log.Println("服务器已安全退出")
}

信号:

本demo中,需要掌握信号(signal)。
信号的本质就是操作系统于进程之间的 “通知机制” 。
用来告诉进程进行特定的操作(如:退出、暂停、重启...啥的)

每个信号,都有特定的编号和含义,就如下:

常用信号表:

信号名信号编号kill 命令写法含义与默认行为是否可被捕获?常见场景
SIGINT2kill -2 PID 或 Ctrl+C中断信号(“优雅请求退出”)用户按 Ctrl+C 终止前台进程
SIGTERM15kill PID(默认)或 kill -15 PID终止信号(“友好退出请求”)系统 / 工具默认的 “停止进程” 信号
SIGKILL9kill -9 PID强制杀死信号(“不可抗拒的死亡命令”)进程卡死时强制清理

注意,不是所有信号都可以被捕获的,如kill -9 ,根本捕获不到,因为进程会直接被杀死。
平时咱们项目正常关闭,都会发送SIGTERM(kill -15)。只有当进程对-15没响应时,才会-9强制杀死。

当然

    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit // 阻塞在这里,直到有信号写入quit通道

这行代码告诉Go,当收到SIGINT(Ctrl+C),或SIGTERM(kill命令默认信号)时,
会把信号发送到quit。
代码不在阻塞,可以正常进行下去,最终来到Context这里。

此后可以通过上下文设置context:
1、可以限制优雅关闭的最大等待时间
2、支持主动取消优雅关闭

官方源码:

package mainimport ("context""log""net/http""os""os/signal"
)func main() {var srv http.ServeridleConnsClosed := make(chan struct{})go func() {sigint := make(chan os.Signal, 1)signal.Notify(sigint, os.Interrupt)<-sigint// We received an interrupt signal, shut down.if err := srv.Shutdown(context.Background()); err != nil {// Error from closing listeners, or context timeout:log.Printf("HTTP server Shutdown: %v", err)}close(idleConnsClosed)}()if err := srv.ListenAndServe(); err != http.ErrServerClosed {// Error starting or closing listener:log.Fatalf("HTTP server ListenAndServe: %v", err)}<-idleConnsClosed
}
func (s *Server) Shutdown(ctx context.Context) error

官方文档翻译:
关机将在不中断任何活动连接的情况下优雅地关闭服务器。Shutdown的工作原理是首先关闭所有打开的侦听器,然后关闭所有空闲连接,然后无限期地等待连接返回空闲状态,然后关闭。如果提供的上下文在关闭完成之前过期,则shutdown返回上下文的错误,否则返回关闭服务器的底层侦听器所返回的任何错误。

当Shutdown被调用时,Serve, ServeTLS, ListenAndServe和ListenAndServeTLS会立即返回ErrServerClosed。确保程序没有退出,而是等待Shutdown返回。

关闭不试图关闭或等待被劫持的连接,如WebSockets。如果需要的话,Shutdown的调用者应该单独通知这些长寿命连接关闭,并等待它们关闭。看到服务器。注册关机通知函数的方法。

一旦在服务器上调用Shutdown,它可能不会被重用;以后对Serve等方法的调用将返回ErrServerClosed。


借鉴:

https://pkg.go.dev/net/http#Server.Shutdown


感谢支持,后续会继续深入讲解

http://www.dtcms.com/a/361894.html

相关文章:

  • 动态配置最佳实践:Spring Boot 十种落地方式与回滚审计指南(含实操与避坑)
  • 如何将mysql数据导入人大金仓数据库
  • 漏洞挖掘 渗透测试思路图总结
  • 期货交易策略自动化实现
  • 数组基础及原理
  • 秋招冲刺计划(Day12)
  • Qwen-Image-Edit完全指南:实战20B参数模型的文字与语义-外观双重编辑
  • 如何使用VMware创建一台Ubuntu机器
  • Linux内核内存管理系列博客教程学习规划
  • KVM虚拟机快速安装与配置指南
  • leetcode算法day24
  • 安科瑞能源管理系统支撑低碳园区节能降碳发展
  • 【前端:Html】--4.进阶:媒体
  • K8S 知识框架和命令操作
  • 刷题之链表oj题目
  • 学习JavaScript的第一个简单程序:Hello World
  • Vue3响应式陷阱:如何避免ref解构导致的响应式丢失
  • ansible知识点总结1
  • Rviz-Gazebo联动
  • C++ 类型系统浅析:值类别与引用类型
  • 工业飞拍技术:高速生产线的 “动态抓拍神器”,到底牛在哪?
  • Java面试宝典:Redis高并发高可用(主从复制、哨兵)
  • oracle默认事务隔离级别
  • ArcGIS 4.x 绘图
  • 开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存
  • 164.在 Vue3 中使用 OpenLayers 加载 Esri 地图(多种形式)
  • Python核心技术开发指南(033)——函数的嵌套
  • matlab扫雷小游戏
  • 计算机组成原理易混知识点
  • Python3环境搭建教程 - 使用Conda工具