首页 > 其他分享 >优雅连接

优雅连接

时间:2023-04-17 10:57:09浏览次数:48  
标签:关机 syscall SIGINT 优雅 信号 gin 连接

优雅连接

什么是优雅关机?

优雅关机就是服务端关机命令发出后不是立即关机,而是等待当前还在处理的请求全部处理完毕后再退出程序,是一种对客户端友好的关机方式。而执行Ctrl+C关闭服务端时,会强制结束进程导致正在访问的请求出现问题。

如何实现优雅关机?

Go 1.8版本之后, http.Server 内置的 Shutdown() 方法就支持优雅地关机,具体示例如下:

// +build go1.8

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "Welcome Gin Server")
	})

	srv := &http.Server{
		Addr:    ":8080",
		Handler: router,
	}

	go func() {
		// 开启一个goroutine启动服务
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 等待中断信号来优雅地关闭服务器,为关闭服务器操作设置一个5秒的超时
	quit := make(chan os.Signal, 1) // 创建一个接收信号的通道
	// kill 默认会发送 syscall.SIGTERM 信号
	// kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号
	// kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它
	// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)  // 此处不会阻塞
	<-quit  // 阻塞在此,当接收到上述两种信号时才会往下执行
	log.Println("Shutdown Server ...")
	// 创建一个5秒超时的context
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	// 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown: ", err)
	}

	log.Println("Server exiting")
}

如何验证优雅关机的效果呢?

上面的代码运行后会在本地的8080端口开启一个web服务,它只注册了一条路由/,后端服务会先sleep 5秒钟然后才返回响应信息。

我们按下Ctrl+C时会发送syscall.SIGINT来通知程序优雅关机,具体做法如下:

  1. 打开终端,编译并执行上面的代码
  2. 打开一个浏览器,访问127.0.0.1:8080/,此时浏览器白屏等待服务端返回响应。
  3. 在终端迅速执行Ctrl+C命令给程序发送syscall.SIGINT信号
  4. 此时程序并不立即退出而是等我们第2步的响应返回之后再退出,从而实现优雅关机。

优雅地重启

优雅关机实现了,那么该如何实现优雅重启呢?

我们可以使用 fvbock/endless 来替换默认的 ListenAndServe启动服务来实现, 示例代码如下:

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/fvbock/endless"
	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "hello gin!")
	})
	// 默认endless服务器会监听下列信号:
	// syscall.SIGHUP,syscall.SIGUSR1,syscall.SIGUSR2,syscall.SIGINT,syscall.SIGTERM和syscall.SIGTSTP
	// 接收到 SIGHUP 信号将触发`fork/restart` 实现优雅重启(kill -1 pid会发送SIGHUP信号)
	// 接收到 syscall.SIGINT或syscall.SIGTERM 信号将触发优雅关机
	// 接收到 SIGUSR2 信号将触发HammerTime
	// SIGUSR1 和 SIGTSTP 被用来触发一些用户自定义的hook函数
	if err := endless.ListenAndServe(":8080", router); err!=nil{
		log.Fatalf("listen: %s\n", err)
	}

	log.Println("Server exiting")
}

如何验证优雅重启的效果呢?

我们通过执行kill -1 pid命令发送syscall.SIGINT来通知程序优雅重启,具体做法如下:

  1. 打开终端,go build -o graceful_restart编译并执行./graceful_restart,终端输出当前pid(假设为43682)
  2. 将代码中处理请求函数返回的hello gin!修改为hello q1mi!,再次编译go build -o graceful_restart
  3. 打开一个浏览器,访问127.0.0.1:8080/,此时浏览器白屏等待服务端返回响应。
  4. 在终端迅速执行kill -1 43682命令给程序发送syscall.SIGHUP信号
  5. 等第3步浏览器收到响应信息hello gin!后再次访问127.0.0.1:8080/会收到hello q1mi!的响应。
  6. 在不影响当前未处理完请求的同时完成了程序代码的替换,实现了优雅重启。

但是需要注意的是,此时程序的PID变化了,因为endless 是通过fork子进程处理新请求,待原进程处理完当前请求后再退出的方式实现优雅重启的。所以当你的项目是使用类似supervisor的软件管理进程时就不适用这种方式了。

总结

无论是优雅关机还是优雅重启归根结底都是通过监听特定系统信号,然后执行一定的逻辑处理保障当前系统正在处理的请求被正常处理后再关闭当前进程。使用优雅关机还是使用优雅重启以及怎么实现,这就需要根据项目实际情况来决定了。

标签:关机,syscall,SIGINT,优雅,信号,gin,连接
From: https://www.cnblogs.com/HJZ114152/p/17325073.html

相关文章

  • 从零开始USRP 04 连接硬件的时候的一些教训
    在连接硬件的时候出现了一堆问题,大概记录一下。我用的代码是:https://www.cnblogs.com/loveandninenine/p/17286194.html然而,连接到硬件上,收端收不到,发射端发不出去,哈哈哈哈哈哈呕了。先说一下我的硬件连接。我一共用了三台USRP,其中A设备负责发射,BC设备负责接收。BC之间做了同......
  • Qt 连接 mysql 报错 QSqlDatabase: MYSQL driver not loaded
    参考: https://blog.csdn.net/o___GRoot/article/details/111320313 https://blog.csdn.net/sksukai/article/details/105344308 我的解决步骤:1.指定qmake qmake:couldnotexec‘/usr/lib/x86_64-linux-gnu/qt4/bin/qmake’:Nosuchfileordirectory需要修改如下......
  • Linux系统知识(十一)-Ubuntu使用TCP/UDP并限制最大连接数
    一、Ubuntu使用TCP1、使用TCP的命令:  /dev/[tcp|upd]/host/port;例如::cat</dev/tcp/127.0.0.1/222、查看当前监听的端口  -bash:connect:拒绝连接  -bash:/dev/[tcp|upd]/host/port:拒绝连接  例:-bash:connect:Connectionrefused-bash:/dev/tcp/127.......
  • 尝试在window系统下,实现连接appuim+ios环境搭配
    弄这个之前,基本全网都是用mac来完成ios的环境搭配,确实ios设备这个需要在MAC设备下,去跑一个WDA的包。我只是不信,最后没法,还是要在window下搞个虚拟机来装MAC。。。以下是MAC弄好之后,需要安装的环境。Mac终端命令运行:一、下载brew:(用腾讯源的)/bin/zsh-c"$(curl-fsSLhttps:......
  • JMeter 关联、录制脚本、连接数据库
    一、JMeter关联关联:当请求之间有依赖关系,比如一个请求的响应数据是另一个请求的参数数据,这时候就需要用到关联处理。JMeter中常用到的关联方法:正则表达式提取器Xpath提取器JSON提取器1、正则表达式提取器作用:针对任......
  • 使用Python代码远程连接服务器
    目录一、paramiko模块的介绍二、基本使用(用户名密码登录)三、用公钥私钥连接一、paramiko模块的介绍模块介绍使用Python的第三方模块paramiko实现远程连接服务器功能:通过python代码连接服务器并执行相关操作并且支持用户名密码连接和公钥私钥连接模块安装pipinstall......
  • Ubuntu系统硬盘安装到其他的电脑上,网络连接不上问题解决
    把Ubuntu系统硬盘安装到其他的电脑上,网络连接不了在一台i5电脑上安装好ubuntu18.04后,把该系统磁盘安装到另外一台i5电脑上。系统可以成功启动,但是不能正常上网。解决办法如下:1)用下面这个命令查看本台电脑上可用的网络接口$ifconfig-a#查看可用的网络接口$iplinks......
  • 为什么每次建立TCP连接时初始化的序列号都要求不一样
    为了防止历史报文被下一个相同的四元组的连接接受假设每次建立连接。客户端和服务端的初始化序列号都是从0开始的客户端和服务端建立了一个TCP连接,在客户端发送数据包被网络阻塞了然后超时重传了这个数据包,而此时服务端断电重启,之前与客户端建立的连接也消失了,于是收到客户端的数......
  • 只有 3 项的开发人员日常检查清单是优雅的简单
    待办事项列表通常是垃圾。他们只会变得更长。你有一种一直在跑步机上的感觉。对于大多数开发人员来说,我们永远不会完成我们的待办事项列表。从这个意义上说,待办事项清单是一种令人沮丧的生产力工具。但是,如果您提炼出一份最重要、最可完成的任务清单,会怎样呢?想象一下,使用更简......
  • 2023【xm格式转不了mp3】教你正确下载喜马拉雅mp3,并优雅获取音频!
    喜马拉雅Windows和Mac客户端下载缓存的音频是xm格式,而且限制只可使用该喜马拉雅软件才能打开,而且经过加密的,因此对于电脑小白来说就不要想着解密xm格式的文件了,而且网上基本没有xm转mp3/m4a的工具。 这次分享的不是如何将xm格式转换成mp3的工具,而是采用了跳过xm格式。直接下载......