首页 > 其他分享 >Go 语言之 Shutdown 关机和fvbock/endless 重启

Go 语言之 Shutdown 关机和fvbock/endless 重启

时间:2023-06-18 19:33:27浏览次数:40  
标签:http Shutdown syscall fvbock Server srv 信号 Go

Go 语言之 Shutdown 关机和fvbock/endless 重启

Shutdown 源码

// Shutdown gracefully shuts down the server without interrupting any
// active connections. Shutdown works by first closing all open
// listeners, then closing all idle connections, and then waiting
// indefinitely for connections to return to idle and then shut down.
// If the provided context expires before the shutdown is complete,
// Shutdown returns the context's error, otherwise it returns any
// error returned from closing the Server's underlying Listener(s).
//
// When Shutdown is called, Serve, ListenAndServe, and
// ListenAndServeTLS immediately return ErrServerClosed. Make sure the
// program doesn't exit and waits instead for Shutdown to return.
//
// Shutdown does not attempt to close nor wait for hijacked
// connections such as WebSockets. The caller of Shutdown should
// separately notify such long-lived connections of shutdown and wait
// for them to close, if desired. See RegisterOnShutdown for a way to
// register shutdown notification functions.
//
// Once Shutdown has been called on a server, it may not be reused;
// future calls to methods such as Serve will return ErrServerClosed.
func (srv *Server) Shutdown(ctx context.Context) error {
	srv.inShutdown.Store(true)

	srv.mu.Lock()
	lnerr := srv.closeListenersLocked()
	for _, f := range srv.onShutdown {
		go f()
	}
	srv.mu.Unlock()
	srv.listenerGroup.Wait()

	pollIntervalBase := time.Millisecond
	nextPollInterval := func() time.Duration {
		// Add 10% jitter.
		interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase/10)))
		// Double and clamp for next time.
		pollIntervalBase *= 2
		if pollIntervalBase > shutdownPollIntervalMax {
			pollIntervalBase = shutdownPollIntervalMax
		}
		return interval
	}

	timer := time.NewTimer(nextPollInterval())
	defer timer.Stop()
	for {
		if srv.closeIdleConns() {
			return lnerr
		}
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-timer.C:
			timer.Reset(nextPollInterval())
		}
	}
}

Shutdown 关机实操

package main

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

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

func main() {
	// Default返回一个Engine实例,其中已经附加了Logger和Recovery中间件。
	router := gin.Default()
	// GET is a shortcut for router.Handle("GET", path, handlers).
	router.GET("/", func(c *gin.Context) {
		// Sleep暂停当前例程至少持续时间d。持续时间为负或为零将导致Sleep立即返回。
		time.Sleep(5 * time.Second)
		// String将给定的字符串写入响应体。
		c.String(http.StatusOK, "Welcome Gin Server")
	})

	// 服务器定义运行HTTP服务器的参数。Server的零值是一个有效的配置。
	srv := &http.Server{
		// Addr可选地以“host:port”的形式指定服务器要监听的TCP地址。如果为空,则使用“:http”(端口80)。
		// 服务名称在RFC 6335中定义,并由IANA分配
		Addr:    ":8080",
		Handler: router,
	}

	go func() {
		// 开启一个goroutine启动服务,如果不用 goroutine,下面的代码 ListenAndServe 会一直接收请求,处理请求,进入无限循环。代码就不会往下执行。

		// ListenAndServe监听TCP网络地址srv.Addr,然后调用Serve来处理传入连接上的请求。接受的连接配置为使TCP能保持连接。
		// ListenAndServe always returns a non-nil error. After Shutdown or Close,
		// the returned error is ErrServerClosed.
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err) // Fatalf 相当于Printf()之后再调用os.Exit(1)。
		}
	}()

	// 等待中断信号来优雅地关闭服务器,为关闭服务器操作设置一个5秒的超时

	// make内置函数分配并初始化(仅)slice、map或chan类型的对象。
	// 与new一样,第一个参数是类型,而不是值。
	// 与new不同,make的返回类型与其参数的类型相同,而不是指向它的指针
	// Channel:通道的缓冲区用指定的缓冲区容量初始化。如果为零,或者忽略大小,则通道未被缓冲。

	// 信号 Signal 表示操作系统信号。通常的底层实现依赖于操作系统:在Unix上是syscall.Signal。
	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

	// Notify使包信号将传入的信号转发给c,如果没有提供信号,则将所有传入的信号转发给c,否则仅将提供的信号转发给c。
	// 包信号不会阻塞发送到c:调用者必须确保c有足够的缓冲空间来跟上预期的信号速率。对于仅用于通知一个信号值的通道,大小为1的缓冲区就足够了。
	// 允许使用同一通道多次调用Notify:每次调用都扩展发送到该通道的信号集。从集合中移除信号的唯一方法是调用Stop。
	// 允许使用不同的通道和相同的信号多次调用Notify:每个通道独立地接收传入信号的副本。
	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秒就超时退出

	// 关机将在不中断任何活动连接的情况下优雅地关闭服务器。
	// Shutdown的工作原理是首先关闭所有打开的侦听器,然后关闭所有空闲连接,然后无限期地等待连接返回空闲状态,然后关闭。
	// 如果提供的上下文在关闭完成之前过期,则shutdown返回上下文的错误,否则返回关闭服务器的底层侦听器所返回的任何错误。
	// 当Shutdown被调用时,Serve, ListenAndServe和ListenAndServeTLS会立即返回ErrServerClosed。确保程序没有退出,而是等待Shutdown返回。
	// 关闭不试图关闭或等待被劫持的连接,如WebSockets。如果需要的话,Shutdown的调用者应该单独通知这些长寿命连接关闭,并等待它们关闭。
	// 一旦在服务器上调用Shutdown,它可能不会被重用;以后对Serve等方法的调用将返回ErrServerClosed。
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown: ", err)
	}

	log.Println("Server exiting")
}

运行

Code/go/shutdown_demo via 

标签:http,Shutdown,syscall,fvbock,Server,srv,信号,Go
From: https://www.cnblogs.com/QiaoPengjun/p/17489629.html

相关文章

  • 2023跟我一起学设计模式:Golang 抽象工厂模式讲解和代码示例
    Golang抽象工厂模式讲解和代码示例抽象工厂是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。抽象工厂定义了用于创建不同产品的接口,但将实际的创建工作留给了具体工厂类。每个工厂类型都对应一个特定的产品变体。在创建产品时,客户端代码调用的是工厂对象的......
  • MongoDB4.0.0 远程连接及用户名密码认证登陆配置
    https://blog.csdn.net/qq_26896281/article/details/81206492参考文章:https://docs.mongodb.com/manual/tutorial/enable-authentication/https://docs.mongodb.com/manual/core/security-built-in-roles/https://blog.csdn.net/Keith003/article/details/80897085一、创建......
  • djangorestframework 学习
    创建记账apppythonmanage.pystartapprecord首先需要确认数据库表结构,分两张表:分类表、账单表分类表包括:分类名称账单表包括:消费金额、消费时间、消费用户(关联到用户表)、消费分类(关联到分类表)、消费的具体描述。根据以上信息创建Model:fromdjango.dbimportmodelsfro......
  • 在Mac上编译可运行在Linux, Windows上的GO程序
     编译运行在amd64位linux系统CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuild 编译运行在amd64位windows系统CGO_ENABLED=0GOOS=windowsgobuild ......
  • Dagonfly 镜像分发:提高容器部署效率的利器
    随着容器技术的快速发展,越来越多的企业和开发者开始将应用程序打包成容器镜像,并使用容器编排工具进行部署和管理。然而,随着容器数量的增加,容器镜像的分发和部署效率成为一个挑战。在这种情况下,Dagonfly镜像分发技术应运而生。Dagonfly是一个开源的镜像分发系统,旨在提供高效、稳......
  • GO通道:无缓冲通道与缓冲通道
    转载请注明出处:1.通道定义在多个协程之间进行通信和管理,可以使用Go语言提供的通道(Channel)类型。通道是一种特殊的数据结构,可以在协程之间进行传递数据,从而实现协程之间的通信和同步。多个协程可以同时读写同一个通道,通过通道来进行数据的传递和共享。通道遵循先......
  • GO 协程
    转载请注明出处:线程是进程中的一个实体,被系统独立调度和分派的基本单位。线程自己不拥有系统资源,只拥有运行中必不可少的资源。同一进程中的多个线程并发执行,这些线程共享进程所拥有的资源。协程是一种比线程更加轻量级的存在,重要的是,协程不被操作系统内核管理,协程完全......
  • Python第三方模块:pymongo模块的用法
    pymongo模块是python操作mongo数据的第三方模块,记录一下常用到的简单用法。首先需要连接数据库:MongoClient():该方法第一个参数是数据库所在地址,第二个参数是数据库所在的端口号authenticate():该方法第一个参数是数据库的账号,第二个参数是数据库的密码frompymongoimpor......
  • Go 语言之 Viper 的使用
    Go语言之Viper的使用Viper介绍Viper:https://github.com/spf13/viper安装gogetgithub.com/spf13/viperViper是什么?Viper是一个针对Go应用程序的完整配置解决方案,包括12-Factor应用程序。它可以在应用程序中工作,并且可以处理所有类型的配置需求和格式。它支持:Vi......
  • UnfairSugoroku
    [ABC298E]UnfairSugoroku考虑令\(f[A][B][0/1]\)表示第一/二个人投完,一、二两人数字为\(A,B\)的概率。\[f[A][B][0]=\dfrac{1}{P}\sum_{i=1}^Pf[A-i][B][1]\]\[f[A][B][1]=\dfrac{1}{Q}\sum_{i=1}^Qf[A][B-i][0]\]复杂度\(O((N+P)(N+Q)(P+Q))\)。转移到\(A,B\)中有......