目 录CONTENT

文章目录
Go

优雅的关掉Golang的项目!

Hello!你好!我是村望~!
2023-02-21 / 0 评论 / 0 点赞 / 213 阅读 / 878 字
温馨提示:
我不想探寻任何东西的意义,我只享受当下思考的快乐~

参考链接

什么是优雅关机?

R-C

我们编写的Web项目部署之后,经常会因为需要进行配置变更或功能迭代而重启服务,单纯的kill -9 pid的方式会强制关闭进程,这样就会导致服务端当前正在处理的请求失败

Feb-21-2023 18-35-48

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

涉及到的API

Notify

https://pkg.go.dev/os/signal#Notify

第一个参数是一个管道 Chanel !

剩下的可变参数是可以传入一些要监听的信号!

如果可变参数传入了一些要监听的信号,那么只有捕获这些监听信号才会被转发到Chanel中!

如果没有提供信号,所有传入的信号都会被转发到 Chanel

func Notify(c chan<- os.Signal, sig ...os.Signal)
kill 默认会发送 syscall.SIGTERM 信号
kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号
kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它

Http.Server

gin内部启动的http服务,其实是实例化了一个标准库的httpServer!

// gin.go
func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()

	if engine.isUnsafeTrustedProxies() {
		debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
			"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
	}

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine.Handler())
	return
}
// server.go
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

func (srv *Server) ListenAndServe() error 

为了能从代码层面控制启动和关闭,我们就实例化一个我们自己的 Server 实例了!

// router.Run(":3000")
// 这里我们需要一个Server实例
srv := &http.Server{
	Addr:    "localhost:3000",
	Handler: router,
}

功能实现

package main

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

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

func main() {
	router := gin.Default()

	// 设置一个 5s 后才能响应的请求!
	router.GET("/", func(ctx *gin.Context) {
		time.Sleep(time.Second * 5)
		ctx.JSON(200, gin.H{
			"MESSAGE": "OK",
		})
	})
	// router.Run(":3000")
	// 这里我们需要一个Server实例
	srv := &http.Server{
		Addr:    "localhost:3000",
		Handler: router,
	}
	// 使用一个 goroutine 跑 web服务,因为ListenAndServe会阻塞main,下面的监听代码执行不到!
	go func() {
		err := srv.ListenAndServe()
		if err != nil {
			fmt.Println(err.Error())
		}
	}()

	quit := make(chan os.Signal, 1)                      // 创建一个接收信号的通道
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞
	fmt.Println("监听信号")
	<-quit //如果没有信号发送则会阻塞在这里!
	// 如果能执行到这里,代表我们想要的信号来了!

	ctx := context.Background()
	// 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出
	timeoutCtx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	// Shutdown 接受一个 context 可以控制 Shutdown
	err := srv.Shutdown(timeoutCtx)
	if err != nil {
		fmt.Println(err.Error())
	}
}

Feb-21-2023 18-42-44

0

评论区