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

Go语言热重载和优雅地关闭程序

Go语言热重载和优雅地关闭程序

我们有时会因不同的目的去关闭服务,一种关闭服务是终止操作系统,一种关闭服务是用来更新配置。

我们希望优雅地关闭服务和通过热重载重新加载配置,而这两种方式可以通过信号包来完成。

1、代码实现

package mainimport ("log""net/http""os""os/signal""syscall"
)type Config struct {Message string
}var conf = &Config{Message: "Before hot reload"}func router() {log.Println("starting up....")http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {_, _ = w.Write([]byte(conf.Message))})go func() {log.Fatal(http.ListenAndServe(":8080", nil))}()
}func main() {router()sigCh := make(chan os.Signal, 1)signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)for {multiSignalHandler(<-sigCh)}
}func multiSignalHandler(signal os.Signal) {switch signal {case syscall.SIGHUP:log.Println("Signal:", signal.String())log.Println("After hot reload")conf.Message = "Hot reload has been finished."case syscall.SIGINT:log.Println("Signal:", signal.String())log.Println("Interrupt by Ctrl+C")os.Exit(0)case syscall.SIGTERM:log.Println("Signal:", signal.String())log.Println("Process is killed.")os.Exit(0)default:log.Println("Unhandled/unknown signal")}
}

首先,定义了一个 Config 结构并声明了一个 conf 变量。

type Config struct {Message string
}var conf = &Config{Message: "Before hot reload"}

这里的代码只是一个简单的配置样本,你可以根据自己的需要定义一个复杂的结构。

其次,定义一个路由器函数,用来绑定和监听 8080 端口。在热重载配置完成后,它也被用来显示结果。

func router() {log.Println("starting up....")http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {_, _ = w.Write([]byte(conf.Message))})go func() {log.Fatal(http.ListenAndServe(":8080", nil))}()
}

下一步是服务器关机和热重载配置,当一个服务器关闭时,它应该停止接收新的请求,同时完成正在进行的请求,

返回其响应,然后关闭,在这里使用信号包实现。

sigCh := make(chan os.Signal, 1)

之后,使用 signal.Notify() 一起发送更多的信号。

signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)

当程序被中断时,signal.Notify 将向 sigCh 通道发送一个信号。

syscall.SIGHUP、syscall.SIGINT 和 syscall.SIGTERM 是什么意思?

  • syscall.SIGINT 是用来在 Ctrl+C 时优雅地关闭的,它也相当于 os.Interrupt。
  • syscall.SIGTERM 是常用的终止信号,也是 docker 容器的默认信号,Kubernetes 也使用它。
  • syscall.SIGHUP 用于热重载配置。

如何优雅地关闭,multiSignalHandler(<-sigCh) 被用来接收 chan 值,然后它将决定运行代码的哪一部分。

func multiSignalHandler(signal os.Signal) {switch signal {case syscall.SIGHUP:log.Println("Signal:", signal.String())log.Println("After hot reload")conf.Message = "Hot reload has been finished."case syscall.SIGINT:log.Println("Signal:", signal.String())log.Println("Interrupt by Ctrl+C")os.Exit(0)case syscall.SIGTERM:log.Println("Signal:", signal.String())log.Println("Process is killed.")os.Exit(0)default:log.Println("Unhandled/unknown signal")}
}

2、测试优雅关闭

首先,运行服务器。

$ go run main.go
2023/06/25 09:57:05 starting up....

发送一个curl请求。

$ curl localhost:8080
Before hot reload

先用Ctrl+C测试一下中断。

$ go run main.go
2023/06/25 09:57:05 starting up....
2023/06/25 09:59:45 Signal: interrupt
2023/06/25 09:59:45 Interrupt by Ctrl+C

3、热重载

首先,运行服务器。

$ go run main.go
2023/06/24 22:03:17 starting up....

然后,发送curl请求。

$ curl localhost:8080
Before hot reload

查看进程:

$ lsof -i tcp:8080
COMMAND   PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
main    85193 root    3u  IPv6 13642377      0t0  TCP *:webcache (LISTEN)

使用 kill 杀死进程:

$ kill -SIGHUP 85193
$ go run main.go
2023/06/24 22:03:17 starting up....
2023/06/24 22:06:05 Signal: hangup
2023/06/24 22:06:05 After hot reload

如果直接使用 kill 命令杀死程序:

$ go run main.go
2023/06/24 22:14:11 starting up....$ lsof -i tcp:8080
COMMAND   PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
main    89619 root    3u  IPv6 13669401      0t0  TCP *:webcache (LISTEN)$ kill -9 89619
$ go run main.go
2023/06/24 22:14:11 starting up....
2023/06/24 22:14:50 Signal: terminated
2023/06/24 22:14:50 Process is killed.

4、Go信号库os/signal

在官方介绍中,这个库主要封装信号实现对输入信号的访问,信号主要用于类 Unix 系统。

信号是事件发生时对进程的通知机制,有时也称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的

正常流程,大多数情况下,无法预测信号到达的精确时间。

因为一个具有合适权限的进程可以向另一个进程发送信号,这可以称为进程间的一种同步技术。当然,进程也可以

向自身发送信号。然而,发往进程的诸多信号,通常都是源于内核。引发内核为进程产生信号的各类事件如下:

  • 硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。比如执行一

    条异常的机器语言指令(除0,引用无法访问的内存区域)。

  • 用户键入了能够产生信号的终端特殊字符。如中断字符 (通常是 Control-C)、暂停字符(通常是 Control-Z)。

  • 发生了软件事件。如调整了终端窗口大小,定时器到期等。

4.1 举例说明

package mainimport ("fmt""os""os/signal""syscall"
)func main() {// os.Signal是一个系统信号接收channelc := make(chan os.Signal, 1)// syscall都是一些系统信号signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)for {s := <-cfmt.Printf("get a signal %s", s.String())switch s {case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:fmt.Printf("exit")os.Exit(0)case syscall.SIGHUP:fmt.Printf("reload")default:fmt.Printf("nothing")}}
}

1、首先初始化一个 os.Signal 类型的 channel,我们必须使用缓冲通道,否则在信号发送时如果还没有准备好接

收信号,就有丢失信号的风险。

2、signal.notify 用于监听信号,参数1表示接收信号的 channel,参数2及后面的表示要监听的信号:

  • syscall.SIGHUP 表示终端控制进程结束

  • syscall.SIGQUIT 表示用户发送QUIT字符 (Ctrl+/) 触发

  • syscall.SIGTERM 表示结束进程

  • syscall.SIGINT 表示用户发送INTR字符 (Ctrl+C) 触发

3、<-c 一直阻塞直到接收到信号退出。

对于上面的的程序是优雅的退出守护进程,接下来就是一些释放资源或dump进程当前状态或记录日志的动作,完

成这些后,主进程退出。

4.2 GO的信号类型

4.2.1 POSIX.1-1990标准中定义的信号列表
信号值动作说明
SIGHUP1Term终端控制进程结束(终端连接断开)
SIGINT2Term用户发送INTR字符(Ctrl+C)触发
SIGQUIT3Core用户发送QUIT字符(Ctrl+/)触发
SIGILL4Core非法指令(程序错误、试图执行数据段、栈溢出等)
SIGABRT6Core调用abort函数触发
SIGFPE8Core算术运行错误(浮点运算错误、除数为零等)
SIGKILL9Term无条件结束程序(不能被捕获、阻塞或忽略)
SIGSEGV11Core无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGPIPE13Term消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGALRM14Term时钟定时信号
SIGTERM15Term结束程序(可以被捕获、阻塞或忽略)
SIGUSR130,10,16Term用户保留
SIGUSR231,12,17Term用户保留
SIGCHLD20,17,18Ign子进程结束(由父进程接收)
SIGCONT19,18,25Cont继续执行已经停止的进程(不能被阻塞)
SIGSTOP17,19,23Stop停止进程(不能被捕获、阻塞或忽略)
SIGTSTP18,20,24Stop停止进程(可以被捕获、阻塞或忽略)
SIGTTIN21,21,26Stop后台程序从终端中读取数据时触发
SIGTTOU22,22,27Stop后台程序向终端中写数据时触发
4.2.2 在SUSv2和POSIX.1-2001标准中的信号列表
信号动作说明
SIGTRAP5CoreTrap指令触发(如断点,在调试器中使用)
SIGBUS0,7,10Core非法地址(内存地址对齐错误)
SIGPOLLTermPollable event (Sys V). Synonym for SIGIO
SIGPROF27,27,29Term性能时钟信号(包含系统调用时间和进程占用CPU的时间)
SIGSYS12,31,12Core无效的系统调用(SVr4)
SIGURG16,23,21Ign有紧急数据到达Socket(4.2BSD)
SIGVTALRM26,26,28Term虚拟时钟信号(进程占用CPU的时间)(4.2BSD)
SIGXCPU24,24,30Core超过CPU时间资源限制(4.2BSD)
SIGXFSZ25,25,31Core超过文件大小资源限制(4.2BSD)

信号 SIGKILL 和 SIGSTOP 可能不会被程序捕获,因此不会受此软件包影响。

同步信号是由程序执行中的错误触发的信号:SIGBUS,SIGFPE 和 SIGSEGV。这些只在程序执行时才被认为是同

步的,而不是在使用 os.Process.Kill 或 kill 程序或类似的机制发送时。一般来说,除了如下所述,Go 程序会将同

步信号转换为运行时异常。其余信号是异步信号,它们不是由程序错误触发的,而是从内核或其他程序发送的。

在异步信号中,SIGHUP 信号在程序失去其控制终端时发送。当控制终端的用户按下中断字符(默认为^ C

(Control-C))时,发送 SIGINT 信号。当控制终端的用户按下退出字符时发送 SIGQUIT 信号,默认为^ \

(Control-Backslash)。一般情况下,您可以通过按^ C来使程序简单地退出,并且可以通过按^使堆栈转储退出。

4.3 Kill命令的原理

我们平时在 Linux 系统会 kill 命令来杀死进程,那其中的原理是什么呢。

4.3.1 kill pid

kill pid 的作用是向进程号为 pid 的进程发送 SIGTERM (这是 kill 默认发送的信号),该信号是一个结束进程的信号

且可以被应用程序捕获。若应用程序没有捕获并响应该信号的逻辑代码,则该信号的默认动作是 kill 掉进程。这是

终止指定进程的推荐做法。

4.3.2 kill -9 pid

kill -9 pid 则是向进程号为 pid 的进程发送 SIGKILL (该信号的编号为9),从本文上面的说明可知,SIGKILL 既不能

被应用程序捕获,也不能被阻塞或忽略,其动作是立即结束指定进程。通俗地说,应用程序根本无法感知 SIGKILL

信号,它在完全无准备的情况下,就被收到 SIGKILL 信号的操作系统给干掉了,显然,在这种暴力情况下,应用

程序完全没有释放当前占用资源的机会。事实上,SIGKILL 信号是直接发给 init 进程的,它收到该信号后,负责终

止 pid 指定的进程。在某些情况下(如进程已经 hang 死,无响应正常信号),就可以使用 kill -9 来结束进程。

相关文章:

Go语言热重载和优雅地关闭程序

Go语言热重载和优雅地关闭程序 我们有时会因不同的目的去关闭服务&#xff0c;一种关闭服务是终止操作系统&#xff0c;一种关闭服务是用来更新配置。 我们希望优雅地关闭服务和通过热重载重新加载配置&#xff0c;而这两种方式可以通过信号包来完成。 1、代码实现 package…...

Python实现两个列表相加的方法汇总

1. 使用 “” 运算符 通过 “” 运算符将两个列表相加&#xff0c;得到一个新的列表。例如&#xff1a; list1 [1, 2, 3] list2 [4, 5, 6] result list1 list2 print(result) # [1, 2, 3, 4, 5, 6]2. 使用 extend 方法 使用 extend 方法将一个列表中的元素逐个添加到另…...

debian12.4配置

文章目录 debian12.4配置概述笔记将非root用户添加到sudo组更换国内源配置ssh的客户端访问END debian12.4配置 概述 在虚拟机中装了一个debian12.4, 想配置ssh客户端连接, 出了问题. 配置乱了, 还好长了个心眼, 做了快照. 发现2个问题: debian12.4默认安装完, 有ssh, 先检查…...

linux切换root用户su - root和su root的区别

这里说一下login shell和 no login shell的区别 通过tty客户端登陆的shell就是login shell&#xff0c;通过在图形界面使用ctrlshiftt的方式新建的shell是no login shell login shell 主要读取两个配置文件/etc/profile和~/.bash_profile no login shell 读取的文件和顺序为&am…...

SQL Server Management Studio创建数据表

文章目录 一、建表注意事项1.1 数据类型1.2 建立数据表的基本SQL语法 二、实例说明2.1 创建数据表2.2 实例2 三、标识列和主键示例&#xff1a; 一、建表注意事项 1.1 数据类型 可以看这个去了解数据类型&#xff1a; 1.2 建立数据表的基本SQL语法 建立数据表的基本 SQL 语…...

【AI的未来 - AI Agent系列】【MetaGPT】4.1 细说我在ActionNode实战中踩的那些坑

文章目录 1. MetaGPT 0.5.2 版本的坑1.1 坑一&#xff1a;cannot import name "ActionNode" from "metagpt.actions.action"1.2 坑二&#xff1a;simple_fill 没有参数 schema1.3 坑三&#xff1a;ActionNode一直在循环执行&#xff0c; 2. 升级成 MetaGP…...

Android学习(五):常用控件

Android学习&#xff08;五&#xff09;&#xff1a;常用控件 常用控件 TextViewEditTextButtonRadioButtonImageView 1、TextView控件 1.1、简介 TextView是用于显示文字(字符串)的控件&#xff0c;可在代码中通过设置属性改变文字的大小、颜色、样式等功能。 1.2、示例…...

基于YOLOv8的学生课堂行为检测,引入BRA注意力和Shape IoU改进提升检测能力

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文摘要&#xff1a;介绍了学生课堂行为检测&#xff0c;并使用YOLOv8进行训练模型&#xff0c;以及引入BRA注意力和最新的Shape IoU提升检测能力 1.SCB介绍 摘要&#xff1a;利用深度学习方法自动检测学生的课堂行为是分析学生课堂表…...

【前后端分离与不分离的区别】

Web 应用的开发主要有两种模式&#xff1a; 前后端不分离 前后端分离 理解它们的区别有助于我们进行对应产品的测试工作。 前后端不分离 在早期&#xff0c;Web 应用开发主要采用前后端不分离的方式&#xff0c;它是以后端直接渲染模板完成响应为主的一种开发模式。以前后端不…...

ubuntu-20.04.6-live-server-amd64安装教程-完整版

简介 Ubuntu 20.04.6 Live Server AMD64 安装教程 - 完整版" 提供了详细的指南&#xff0c;旨在帮助用户在使用 AMD64 架构的服务器上安装 Ubuntu 20.04.6 Live Server 版本。该教程包含全面的步骤和详细说明&#xff0c;使用户能够顺利完成整个安装过程&#xff0c;建立…...

C for Graphic:Sliced Circle Image

不做UI不知道&#xff0c;没想到时至今日&#xff0c;ugui居然没有sliced filled image模式&#xff0c;用circle做filled&#xff0c;不能用sliced九宫格图&#xff0c;导致每次使用这个效果必须一张新图&#xff0c;何其浪费资源。 原始功能如下&#xff1a; 我…...

入门级的 DataV 教程,适用于 Vue 2

入门级的 DataV 教程&#xff0c;适用于 Vue 2。这个教程将指导您创建一个名为 datav-project 的 Vue 项目&#xff0c;并展示如何在其中使用 DataV。我们将从安装 Vue CLI 开始&#xff0c;然后创建项目&#xff0c;接着添加 DataV&#xff0c;并最后显示一个简单的数据可视化…...

JVM工作原理与实战(二十一):内存管理

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、不同语言的内存管理 1.C/C的内存管理 2.Java的内存管理 二、垃圾回收的对比 1.自动垃圾回收与手动垃圾回收的对比 2.优点与缺点 总结 前言 JVM作为Java程序的运行环境&#…...

Win10 打开文件突然鼠标变成一个蓝色大圈卡住点不了也打不开文件,重启电脑也是这样

环境: Win10 专业版 加密客户端环境 问题描述: Win10 打开桌面word文件突然鼠标变成一个蓝色大圈卡住点不了也打不开文件,重启电脑也是这样,只有蓝色圈变大没有鼠标指针出现圈卡着不会动,和那些有鼠标箭头加小蓝色圈不一样 解决方案: 某网上查看的,还是要自己排查…...

论文阅读笔记AI篇 —— Transformer模型理论+实战 (四)

论文阅读笔记AI篇 —— Transformer模型理论实战 &#xff08;四&#xff09; 一、理论1.1 理论研读1.2 什么是AI Agent? 二、实战2.1 先导知识2.1.1 tensor的创建与使用2.1.2 PyTorch的模块2.1.2.1 torch.nn.Module类的继承与使用2.1.2.2 torch.nn.Linear类 2.2 Transformer代…...

Template -- Vue2

Vue2 版本 Node 14.14.0Npm 6.14.8Vue vue/cli 5.0.3 npm install -g vue/cli5.0.3 cnpm cnpm7.1.0 npm install -g cnpm7.1.0 --registryhttps://registry.npm.taobao.org 项目 创建 vue create single # vue 2.6.14配置 // vue.config.js const { defineConfi…...

zookeeper window 安装

下载 Apache ZooKeeper 解压Zookeeper安装包到指定目录&#xff0c;注意目录不要有空格。 备份zoo_sample.cfg并改名zoo.cfg 注意&#xff1a;此处的路径一定要使用双斜杠" \\ " D:\\apache-zookeeper-3.8.3-bin\\data 新建环境变量&#xff1a;ZOOKEEPER_HOME D…...

Redis 面试题 | 02.精选Redis高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

大数据开发之kafka(完整版)

第 1 章&#xff1a;Kafka概述 1.1 定义 Kafka是一个分布式的基于发布/订阅模式的消息队列&#xff0c;主要应用于大数据实时处理领域。 发布/订阅&#xff1a;消息的发布者不会将消息直接发送给特定的订阅者&#xff0c;而是将发布的消息分为不同的类别&#xff0c;订阅者只…...

单体架构、微服务和无服务器架构

前言 在这篇文章中&#xff0c;我将演示在决定使用单体架构、微服务架构和无服务器架构时的权衡的简化心智模型。目标是突显每种风格的固有优势和缺陷&#xff0c;并提供关于何时选择哪种架构风格的指导。 单体架构 对于小团队或项目来说是理想的入门架构。它简单易上手&…...

Github仓库使用方式

主要参考&#xff1a; 「详细教程」使用git将本地项目上传至Github仓库&#xff08;MacOS为例&#xff09;_github上传代码到仓库-CSDN博客 新建文件夹参考&#xff1a; GitHub使用指南——建立仓库、建立文件夹、上传图片详细教程-CSDN博客 一、新建一个 github 仓库&#…...

Harmony Ble蓝牙App(四)描述符

Harmony Ble蓝牙App&#xff08;四&#xff09;描述符 前言正文一、优化二、描述① 概念② 描述提供者③ 显示描述符 三、源码 前言 上一篇中了解了特性和属性&#xff0c;同时显示设备蓝牙服务下的特性和属性&#xff0c;本文中就需要来使用这些特性和属性来完成一些功能。 正…...

C# 实现单线程异步互斥锁

文章目录 前言一、异步互斥锁的作用是什么&#xff1f;示例一、创建和销毁 二、如何实现&#xff1f;1、标识&#xff08;1&#xff09;标识是否锁住&#xff08;2&#xff09;加锁&#xff08;3&#xff09;解锁 2、异步通知&#xff08;1&#xff09;创建对象&#xff08;2&a…...

Java设计模式中策略模式可以解决许多if-else的代码结构吗? 是否能满足开闭原则?

Java设计模式中策略模式可以解决许多if-else的代码结构吗&#xff1f; 是否能满足开闭原则&#xff1f; 是的&#xff0c;策略模式可以帮助解决许多if-else的代码结构。通过将不同的算法封装成不同的策略类&#xff0c;然后在需要的时候动态地切换策略&#xff0c;可以避免使…...

[C#]C# winform部署yolov8目标检测的openvino模型

【官方框架地址】 https://github.com/ultralytics/ultralytics 【openvino介绍】 OpenVINO&#xff08;Open Visual Inference & Neural Network Optimization&#xff09;是由Intel推出的&#xff0c;用于加速深度学习模型推理的工具套件。它旨在提高计算机视觉和深度学…...

力扣刷MySQL-第五弹(详细讲解)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…...

用C语言实现简单的三子棋游戏

目录 1 -> 模块简介 2 -> test.c 3 -> game.c 4 -> game.h 1 -> 模块简介 test.c:测试游戏逻辑 game.c: 函数的实现 game.h:函数的声明 2 -> test.c #define _CRT_SECURE_NO_WARNINGS 1#include "game.h";void menu() {printf("****…...

Yaklang 中的类型和变量

Yaklang 的类型其实非常简单&#xff0c;我们仅需要记住如下类型即可 string 字符串类型&#xff0c;用以快速构建一个字符串int 整数类型&#xff1a;在 64 位机中&#xff0c;int 和 int64 是一样的float 浮点类型&#xff0c;用来定义和表示浮点数byte 本质上等同于 uint8u…...

C语言从入门到实战——编译和链接

编译和链接 前言一、 翻译环境和运行环境二、 翻译环境2.1 预处理&#xff08;预编译&#xff09;2.2 编译2.2.1 词法分析2.2.2 语法分析2.2.3 语义分析 2.3 汇编2.4 链接 三、 运行环境 前言 在C语言中&#xff0c;编译和链接是将源代码转换为可执行文件的两个主要步骤。 编…...

【实战教程】ThinkPHP6分页功能轻松实现,让你的网站更高效!

ThinkPHP是一款非常流行的PHP开发框架&#xff0c;其最新版本ThinkPHP6在性能和易用性方面都得到了很大的改善。分页功能是网页开发中非常常见的功能&#xff0c;而ThinkPHP6也提供了非常方便的分页方法。本文将介绍如何实现ThinkPHP6的分页功能。 一、了解分页功能 在Web应用…...