手把手带你写一个 Web 框架
叶剑峰
腾讯高级工程师,前滴滴技术专家
22731 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 42 讲
特别放送 (1讲)
手把手带你写一个 Web 框架
15
15
1.0x
00:00/00:00
登录|注册

06|重启:如何进行优雅关闭?

验证
源码分析
思考题
server.Shutdown方法
net/http在Golang 1.8版本之前的解决方案
os/signal库
Kill命令
Ctrl+\
Ctrl+C
小结
如何等待所有逻辑都处理结束
如何控制关闭进程的操作
什么是优雅关闭
优雅关闭服务
重启服务的复杂性
重启服务的必要性
启动服务器
学习内容
作者:轩脉刃
标题:重启:如何进行优雅关闭?
参考文章
重启:如何进行优雅关闭?

该思维导图由 AI 生成,仅供参考

你好,我是轩脉刃。
通过前面几节课的学习,我们已经能启动一个按照路由规则接收请求、进入控制器计算逻辑的服务器了。
但是,在实际业务开发过程中,功能和需求一定是不断迭代的,在迭代过程中,势必需要重启服务,这里的重启就是指一个关闭、启动进程的完成过程。
目前所有服务基本都无单点问题,都是集群化部署。对一个服务的关闭、启动进程来说,启动的流程基本上问题不大,可以由集群的统一管理器,比如 Kubernetes,来进行服务的启动,启动之后慢慢将流量引入到新启动的节点,整个服务是无损的。
但是在关闭服务的过程中,要考虑的情况就比较复杂了,比如说有服务已经在连接请求中怎么办?如果关闭服务的操作超时了怎么办?所以这节课我们就来研究下如何优雅关闭一个服务。

如何优雅关闭

什么叫优雅关闭?你可以对比着想,不优雅的关闭比较简单,就是什么都不管,强制关闭进程,这明显会导致有些连接被迫中断。
或许你并没有意识到这个问题的严重性,不妨试想下,当一个用户在购买产品的时候,由于不优雅关闭,请求进程中断,导致用户的钱包已经扣费了,但是商品还未进入用户的已购清单中。这就会给用户带来实质性的损失。
所以,优雅关闭服务,其实说的就是,关闭进程的时候,不能暴力关闭进程,而是要等进程中的所有请求都逻辑处理结束后,才关闭进程。按照这个思路,需要研究两个问题“如何控制关闭进程的操作” 和 “如何等待所有逻辑都处理结束”。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何在Golang中实现优雅关闭服务的方法。通过控制关闭进程的操作和等待所有逻辑处理结束,可以确保服务重启过程中不会影响用户体验。文章首先讨论了如何捕获信号并使用os/signal库的Notify方法来实现对信号量的控制。其次,介绍了在Golang 1.8版本之后引入的net/http的server.Shutdown方法,该方法可以阻塞当前Goroutine,并在所有连接请求都结束后才继续执行。通过对server.Shutdown源码的解析,读者可以了解其实现原理和细节。最后,通过验证和小结,读者可以加深对优雅关闭服务的理解,并在实际应用中进行相应的修改和控制。整体而言,本文内容详实,逻辑清晰,对于Golang开发者来说是一篇有价值的技术文章。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 Web 框架》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(15)

  • 最新
  • 精选
  • The brain is a good thing
    收到结束信号后,还会接受新的请求吗,这样无限请求进来是不是结束不了?

    作者回复: 不会的,有个inShutdown的标记位,标记了就没有请求进来了

    2023-02-20归属地:广东
  • ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 即可

    作者回复: yes,u are right

    2021-12-03
  • 安喆||工程师||电器技术咨询...
    老师,这个课程的开发环境是什么,如何搭建?

    编辑回复: 第1节课有详细说明,Golang 源码的版本是 1.15.5,实现的步骤就是按文章顺序读,代码可以参考文中的代码和GitHub地址。

    2021-09-24
  • 贺鹏
    quit := make(chan os.Signal) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) <-quit ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatal("server shutdown error:", err) } select { case <-ctx.Done(): log.Println("timeout of 5 seconds") } log.Println("server exiting")
    2021-09-26
    2
    6
  • void
    “它们发送的信号是进入 main 函数的,即只有 main 函数所在的 Goroutine 会接收到,所以必须在 main 函数所在的 Goroutine 监听信号。” 这个说法不太对吧? 别的goroutine里也是可以收到信号的,只不过在主协程里接收信息比较容易收尾后退出整个进程。
    2021-11-12
    5
  • 陈亦凡
    第一节已经值回票价,希望老师快点更新
    2021-09-26
    4
  • Geek_5244fa
    必须在 main 函数所在的 Goroutine 监听信号吗?在另一个 goroutine 理处理也可以吧: func main() { done := make(chan struct{}) go func() { quit := make(chan os.Signal) signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) fmt.Printf("singal %v", <-quit) close(done) }() <-done }
    2021-09-24
    3
  • 语法糖
    "10s 内在控制台执行 Ctrl+C 关闭程序观察控制台程序是否不会立刻结束,而是在 10s 后结束" 前提是请求开始到ctrl+c 之间超过5s, 否则留给剩余请求的时间大于5s, 超过了服务器关闭的最大超时5s, 会强制关闭服务器
    2022-10-15归属地:江苏
    1
    2
  • 芒果少侠
    自己的答案,看完后发现skyhackvip同学的写法似乎更好,不用多起一个协程 quitSig := make(chan os.Signal) signal.Notify(quitSig, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) <-quitSig cancelableCtx, cancel := context.WithCancel(context.Background()) inAdvance := time.After(time.Second * 5) go func() { select { case <-inAdvance: log.Print("server shutdown exceeds timeout") cancel() } }() if err := server.Shutdown(cancelableCtx); err != nil { log.Fatal("server shutdown failed: ", err) }
    2021-10-03
    1
  • Geek_eebc7f
    func mian(){ .... cancelContext, _ := context.WithTimeout(context.Background(), time.Second*5) if err := server.Shutdown(cancelContext); err != nil { log.Fatal("Server Shutdown:", err) } }
    2023-06-06归属地:俄罗斯
收起评论
显示
设置
留言
15
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部