iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >go实现服务优雅关闭的示例
  • 461
分享到

go实现服务优雅关闭的示例

go服务关闭 2023-02-09 12:02:14 461人浏览 泡泡鱼
摘要

目录为什么需要优雅关闭监控服务退出信号拒绝新请求执行关闭之前的回调等待处理中的请求执行完毕实战支持强制退出为什么需要优雅关闭 什么叫优雅关闭?先说不优雅关闭,就是什么都不管,强制关闭

为什么需要优雅关闭

什么叫优雅关闭?先说不优雅关闭,就是什么都不管,强制关闭进程,这会导致有些正在处理中的请求被强行中断

这样做有什么问题?

  • 用户本次请求会失败,降低用户体验
  • 没有事务数据库操作,会产生部分成功的问题,破坏原子性
  • 某些缓服务需要定期将本地缓存刷到远程db,强制关闭会导致数据丢失

优雅关闭的核心是以下功能:

  • 如何监听退出信号
  • 如何拒绝新请求
  • 如何等待进行中的请求处理完毕

监控服务退出信号

Go中使用下面的代码监听退出信号,如果c返回,说明监听到信号

不同的操作系统监听不同的退出信号

c := make(chan os.Signal, 1)
signals := []os.Signal{
   syscall.SIGINT,
   syscall.SIGTERM,
   syscall.SIGQUIT,
}

signal.Notify(c, signals...)
<-c

拒绝新请求

go在1.8后增加了shutdown方法来,我们看看它怎么实现优雅关闭:

srv.inShutdown.setTrue()
lnerr := srv.closeListenersLocked()
srv.closeDoneChanLocked()
  • 设置inShutdown标志位
  • 关闭所有的listener
  • 关闭doneChan

这一段对应到Http服务接收请求的流程:

for {
   rw, err := l.Accept()
   if err != nil {
      select {
      case <-srv.getDoneChan():
         return ErrServerClosed
   // ...       
}

一旦关闭listener,关闭doneChan后,http服务就不会再接收新的请求,直接返回

执行关闭之前的回调

for _, f := range srv.onShutdown {
   go f()
}

这里的回调实现得比较粗糙:

  • 没有优先级的概念,所有回调并发执行,因此需要保证回调之间没有依赖
  • 虽然回调不适合长时间运行,但Go http没有提供机制来保证这些回调一定能执行完毕,若想做到这点需要自己处理

等待处理中的请求执行完毕

设置标识位可以拒绝新的请求,但依旧在执行的请求还在处理中,需要等这些请求执行完毕

等待处理中的请求执行完毕有两种思路:

  • 等待一段固定的时间
  • 实时维护请求的计数

go选择了两种方式结合的模式,通过ctx设置一个最大的等待时间,同时不断轮询正在请求中的计数

ctx超时或者计数变为0,都会返回

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

这里每隔一定时间检查已有请求是否执行完毕,如果执行完毕,或者外部通过ctx设置的超时到期就会返回

检查间隔是多少?

  • 从1ms开始,每轮检查后倍增,最大500ms

怎么判断是否执行完毕?

  • 所有的连接都关闭
  • 所有的listener都关闭

服务收到监听信号返回之前,关闭连接和listener,会被这里检查到

在这里插入图片描述

实战

func main() {
   // 注册路由
   http.Handle("/aaa", http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
      time.Sleep(time.Second * 10)
      fmt.Println(111)
   }))
   server := http.Server{
      Addr:    "localhost:8080",
      Handler: http.DefaultServeMux,
   }

   close := make(chan int)
   go func() {
      quit := make(chan os.Signal)
      signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
      <-quit

      ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
      defer cancel()
      err := server.Shutdown(ctx)
      log.Print(err)
      
      // 控制外层退出
      close <- 1
   }()

   err := server.ListenAndServe()
   fmt.Println(err)

   <-close
}

该代码做了下面的事:

  • 注册一个10s才返回的路由处理函数
  • 开子协程监听OS的退出信号,如果监听到了开始进行优雅关闭,虽多等待30s
  • 主协程调用 server.ListenAndServe(),开始监听请求

需要注意的是,一定要在子协程中优雅关闭结束后,主协程才能退出,这里用channel控制

因为主协程发现doneChan被关闭时会马上返回,但此时主协程开的业务处理协程还在进行中,如果主协程此时退出,无法达到优雅关闭的效果

按照以下流程测试

  • 启动 WEB 服务
  • 在浏览器请求http://localhost:8080/aaa
  • 过5秒后在控制台按下ctrl+c
  • 观察控制台程序是否不会立刻结束,而是在 10s 后结束

支持强制退出

既然有优雅退出,那就有强制退出,我们假设如果按下两次ctrl+c,代表用户希望服务强制退出:

close := make(chan int, 2)
go func() {
   quit := make(chan os.Signal)
   signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
   <-quit
   go func() {
      <-quit
      os.Exit(1)
   }()

   // ...
}()

做法很简单,收到第一个退出信号后,再开一个子协程,如果再收到退出信号,就调用os.Exit退出进程

并且close channel的容量需要为2,避免当两次退出信号过短时丢失信号

到此这篇关于go实现服务优雅关闭的示例的文章就介绍到这了,更多相关go 服务关闭内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: go实现服务优雅关闭的示例

本文链接: https://www.lsjlt.com/news/195307.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • go实现服务优雅关闭的示例
    目录为什么需要优雅关闭监控服务退出信号拒绝新请求执行关闭之前的回调等待处理中的请求执行完毕实战支持强制退出为什么需要优雅关闭 什么叫优雅关闭?先说不优雅关闭,就是什么都不管,强制关闭...
    99+
    2023-02-09
    go 服务关闭
  • go如何优雅关闭Graceful Shutdown服务
    目录Shutdown方法RegisterOnShutdown方法sync.WaitGroup处理退出函数Shutdown方法 Go1.8之后有了Shutdown方法,用来优雅的关闭(...
    99+
    2023-05-20
    go关闭服务 Graceful Shutdowng
  • nginxpodhook钩子优雅关闭示例详解
    目录一.系统环境二.前言三.pod hook(pod钩子)四.如何优雅的关闭nginx pod一.系统环境 服务器版本docker软件版本Kubernetes(k8s)集群版本CPU...
    99+
    2022-11-13
    nginx pod hook钩子关闭 nginx pod hook
  • Go如何优雅的关闭goroutine协程
    目录1.简介2.为什么需要关闭goroutine2.1 协程的生命周期2.2 协程的终止条件2.3 为什么需要主动关闭goroutine3.如何优雅得关闭goroutine3.1 传...
    99+
    2023-05-20
    Go关闭goroutine协程 Go关闭goroutine Go关闭协程
  • redisson特性及优雅实现示例
    目录redisson的几大特性可重入性阻塞能力续约初始化timer的代码redisson的几大特性 相信看了这个标题的同学,对这个问题以已经非常不陌生了,信手拈来redisson的几...
    99+
    2022-11-13
    redisson特性及实现 redisson特性
  • go语言开发优雅得关闭协程的方法
    本篇文章和大家了解一下go语言开发优雅得关闭协程的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1.简介本文将介绍首先为什么需要主动关闭goroutine,并介绍如何在Go语言中关闭goroutine的常见套路,包括...
    99+
    2023-07-06
  • golang中怎么优雅地关闭http服务
    这篇文章主要介绍“golang中怎么优雅地关闭http服务”,在日常操作中,相信很多人在golang中怎么优雅地关闭http服务问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”golang中怎么优雅地关闭htt...
    99+
    2023-07-06
  • 如何在 Golang 中优雅地关闭 HTTP 服务器
    Golang (又称为 Go) 是一门相对比较年轻的编程语言,它被广泛应用于后端服务和 API 的开发中。而 HTTP 是一个常用的协议,很多 Golang 开发人员会选择使用 Golang 中自带的 HTTP 包来进行 HTTP 服务器的...
    99+
    2023-05-14
    Golang go语言 http
  • 解析golang中如何优雅地关闭http服务
    Golang是一种非常流行的编程语言,它具有高效的并发处理能力和优秀的性能表现。相信许多golang的开发人员都会遇到一个这样的问题,在golang中如何优雅地关闭http服务?首先,我们需要知道,创建一个http服务是比较容易的,只需要几...
    99+
    2023-05-14
  • 怎么在Golang中优雅地关闭HTTP服务器
    这篇“怎么在Golang中优雅地关闭HTTP服务器”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么在Golang中优雅地关...
    99+
    2023-07-05
  • spring-security关闭登录框的实现示例
    事情要从同事的一个项目说起,项目中需要集成公司的单点登录系统,但是无论如何都无法跳转到正常的登录页面。相反,却始终跳转到另外一个登录页面。 但是代码却非常简单,简化一下 @Co...
    99+
    2024-04-02
  • Go如何优雅的使用字节池示例详解
    目录背景为何需要字节池最简单的方式测试不预先申请空间预先申请空间字节数组池测试结果更优雅的方式测试直接使用Bufferbytes.Buffer池测试结果限制池大小测试固定大小字节池测...
    99+
    2024-04-02
  • Go项目实现优雅关机与平滑重启功能
    目录前言什么是优雅关机?实现原理实现优雅重启实现平滑重启测试总结前言 优雅关机就是服务端关机命令发出后不是立即关机,而是等待当前还在处理的请求全部处理完毕后再退出程序,是一种对客户端...
    99+
    2024-04-02
  • 使用golang如何优雅的关机或重启操作示例
    目录前言优雅地关机什么是优雅关机?如何实现优雅关机?优雅地重启总结前言 我们编写的Web项目部署之后,经常会因为需要进行配置变更或功能迭代而重启服务,单纯的kill -9 pid的方...
    99+
    2024-04-02
  • JavaScript实现点击关闭全屏示例详解
    目录引言思路具体实现问题升级引言 今天,我们来探讨的问题是: 当点击图片的时候,我们需要其全屏展示,当我们再次点击的时候,图片退出全屏播放。 PS: 我们退出全屏播放的情况一般是按 ...
    99+
    2024-04-02
  • vue关闭浏览器退出登录的实现示例
    目录1、beforeunload事件2、unload事件3、源码部分  项目需要:也在网上找了不少类似的文章,不过用起来多少有点问题,毕竟要适合自己的需求,我这里是vue3适用,理论...
    99+
    2024-04-02
  • QT中窗口关闭自动销毁的实现示例
    目录qt关于窗口关闭触发函数/信号方法一、方法二、我们知道c++内存管理的一个规则:new出来的对象,一定要delete。 我们实现一个弹窗的时候,有时候继承widget,有时候继承...
    99+
    2024-04-02
  • javaSwing写关闭窗口的提示框实例
    目录javaSwing写关闭窗口的提示框java swing 提示框第一种第二种第三种javaSwing写关闭窗口的提示框 先在该窗体的initUI()中初始化中写一句 // 关...
    99+
    2024-04-02
  • flutter优雅实现扫码枪获取数据源示例详解
    目录前言痛点问题如何在不改动源码的方式下,动态控制焦点是否触发键盘弹出?1.系统键盘弹出的原因2. 如何拦截 methodChannel3. 拦截思路扫码库更新前言 在往期的分享中...
    99+
    2023-01-11
    flutter扫码枪获取数据源 flutter 扫码枪
  • Go微服务网关的实现
    目录Go微服务网关网络基础大纲OSI七层网络协议三次握手 与 四次挥手为啥会出现大量的close_waitTCP 为啥需要拥塞控制TCP 拥塞控制为啥会出现粘包,拆包,如何处理产生t...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作