热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

golang的httpserver优雅重启方法详解

这篇文章主要给大家介绍了关于golang的httpserver优雅重启的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

前言

去年在做golangserver的时候,内部比较头疼的就是在线服务发布的时候,大量用户的请求在发布时候会被重连,在那时候也想了n多的方法,最后还是落在一个github上的项目,facebook的一个golang项目grace,那时候简单研究测试了一下可以就直接在内部使用了起来,这段时间突然想起来,又想仔细研究一下这个项目了。

从原理上来说是这样一个过程:

    1)发布新的bin文件去覆盖老的bin文件

    2)发送一个信号量,告诉正在运行的进程,进行重启

    3)正在运行的进程收到信号后,会以子进程的方式启动新的bin文件

    4)新进程接受新请求,并处理

    5)老进程不再接受请求,但是要等正在处理的请求处理完成,所有在处理的请求处理完之后,便自动退出

    6)新进程在老进程退出之后,由init进程收养,但是会继续服务。

所以一步一步来看,关键是从第2步开始之后怎么做,所以我们先来看看第2步的实现,这个应该说很简单,发送信号量到一个进程,使用kill命令即可,在facebook这个项目中发送的信号量有3个:SIGINT,SIGTERM,SIGUSR2,前面两个信号收到后程序会直接退出,后面一个信号SIGUSR2才会执行所谓的优雅重启。

第3步,正在运行的进程收到SIGUSR2信号后,会以子进程的方式启动新的bin文件。先直接上代码看:https://github.com/facebookgo/grace/blob/master/gracehttp/http.go

func (a *app) signalHandler(wg *sync.WaitGroup) {
 ch := make(chan os.Signal, 10)
 signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
 for {
  sig := <-ch
  switch sig {
  case syscall.SIGINT, syscall.SIGTERM: 
   // this ensures a subsequent INT/TERM will trigger standard go behaviour of
   // terminating. 执行标准的go终止行为,程序就结束了
   signal.Stop(ch)
   a.term(wg)
   return
  case syscall.SIGUSR2: // 这里开始执行优雅重启
   err := a.preStartProcess() 
   // 这个函数在源代码中没有具体实现功能,只是预留了一个钩子函数,用户可以注册自己的函数,可以在重启之前做些自定义的事情。一般情况下也没有什么可以做的,除非有些特殊的服务环境或是状态保存之类的,至少目前,我们的server还没有遇到
   if err != nil {
    a.errors <- err
   }
   // we only return here if there's an error, otherwise the new process
   // will send us a TERM when it's ready to trigger the actual shutdown.
   if _, err := a.net.StartProcess(); err != nil { // 这里开始正式所谓的优雅重启   
    a.errors <- err
   }
  }
 }
}

a.net.StartProcess的过程我们来看看基本过程:

func (n *Net) StartProcess() (int, error) {
 listeners, err := n.activeListeners() // 获取目前在监听的端口,这块也是重点,下面重点介绍
 if err != nil {
  return 0, err
 }
 
 // Extract the fds from the listeners. 从监听端口中把文件描述符取出来
 files := make([]*os.File, len(listeners))
 for i, l := range listeners {
  files[i], err = l.(filer).File()
  if err != nil {
   return 0, err
  }
  defer files[i].Close()
 }
 
 // Use the original binary location. This works with symlinks such that if
 // the file it points to has been changed we will use the updated symlink.
 // 获取可执行bin文件的路劲,也可以是链接路劲,会使用最新的链接路径作为启动文件路劲的
 argv0, err := exec.LookPath(os.Args[0])
 if err != nil {
  return 0, err
 }
 
 // Pass on the environment and replace the old count key with the new one.
 // 获取 LISTEN_FDS 换进变量值 
 var env []string
 for _, v := range os.Environ() {
  if !strings.HasPrefix(v, envCountKeyPrefix) {
   env = append(env, v)
  }
 }
 env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))
 
 allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
 // 这里调用一个golang底层的进程启动函数,来指定,上面获取的参数来启动进程
 process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
  Dir: originalWD,
  Env: env,
  Files: allFiles,
 })
 if err != nil {
  return 0, err
 }
 // 返回新进程id。
 return process.Pid, nil 
}

以上是启动新进程,并且接管监听端口的过程, 一般情况下端口是不可以重复监听的,所以这里就要需要使用比较特别的办法,从上面的代码来看就是读取监听端口的文件描述符,并且把监听端口的文件描述符传递给子进程,子进程里从这个文件描述符实现对端口的监听

另外还有一个比较特别的地方就是老的接口怎么关闭的问题,关闭必须要把已经收到的请求处理完成之后再关闭。为此facebook的同学另外开了一个项目httpdown,继承了原始的httpserver,但是多了对各种链接状态的维护和处理,这部分后面在分析。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。


推荐阅读
  • React 小白初入门
    推荐学习:React官方文档:https:react.docschina.orgReact菜鸟教程:https:www.runoob.c ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Imdevelopinganappwhichneedstogetmusicfilebystreamingforplayinglive.我正在开发一个应用程序,需要通过流 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • ch3中可视化软件pangolin的安装步骤及注意事项
    本文介绍了在ch3中安装可视化软件pangolin的步骤及注意事项。首先提供了pangolin的下载地址,并说明了下载后需要放到与虚拟机交互的文件夹地址。然后详细介绍了安装pangolin所需的依赖项,并提供了在终端进行安装的命令。最后给出了解压pangolin的步骤。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了一些Java开发项目管理工具及其配置教程,包括团队协同工具worktil,版本管理工具GitLab,自动化构建工具Jenkins,项目管理工具Maven和Maven私服Nexus,以及Mybatis的安装和代码自动生成工具。提供了相关链接供读者参考。 ... [详细]
  • 3年半巨亏242亿!商汤高估了深度学习,下错了棋?
    转自:新智元三年半研发开支近70亿,累计亏损242亿。AI这门生意好像越来越不好做了。近日,商汤科技已向港交所递交IPO申请。招股书显示& ... [详细]
author-avatar
查建樺
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有