从nsq中学习如何优雅的退出go 网络程序
退出运行中的程序,可以粗暴的kill -9 $PID,但这样会破坏业务的完整性,有可能一个正在在执行的逻辑半途而费,从而产生不正常的垃圾数据。
本文总结在go语言中,如何能优雅的退出网络应用,涉及的知识包括:signal,channel,WaitGroup等。
从这里:https://gobyexample.com/channel-synchronization 可以简单了解到,在go中如何使用channel实现goroutines同步。
在nsq中,也使用了相同的机制,不过封装更复杂了些。我们以nsqadmin中的实现为例进行简单的分析。
代码段1(来自:https://github.com/bitly/nsq/blob/master/nsqadmin/main.go):
exitChan := make(chan int)
signalChan := make(chan os.Signal, 1)
go func() {
<-signalChan
exitChan <- 1
}()
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
//....
nsqadmin.Main()
<-exitChan
nsqadmin.Exit()
上面的代码正常执行后,会卡到倒数第二句的<-exitChan中,直到exitChan中有数据进入。
当我们通过命令行执行:kill -s SIGINT $PID时,signalChan中收到一个信息,上面第四行代码中的goroutines会停止阻塞,继续向下执行,exitChann中加入一条数据。当exitChann中有了数据,倒数第二句也会停止阻塞,执行nsqadmin.Exit()实现优雅退出。
当然上面的代码,也可以简化:
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
//....
nsqadmin.Main()
<-signalChan
nsqadmin.Exit()
至于nsq为什么没有这么做,还不太清楚。能力有限,体会不到其深意所在。
上面的例子只适用于两个goroutines之间,一个处理完业务后给exitChan写数据,主goroutines卡在exitChan上等数据。(主goroutines必须比处理业务的goroutines后退出)。
如果一个主线程下开了多个子goroutines,使用channel的方式就不够优雅了。可以使用WaitGroup,关于WaitGroup的介绍可以参考:http://www.baiyuxiong.com/?p=913
在nsq中同样使用了WaitGroup实现退出。
代码段2(来自:https://github.com/bitly/nsq/blob/master/util/wait_group_wrapper.go)
type WaitGroupWrapper struct {
sync.WaitGroup
}
func (w *WaitGroupWrapper) Wrap(cb func()) {
w.Add(1)
go func() {
cb()
w.Done()
}()
}
代码段3(来自:<a href="https://github.com/bitly/nsq/blob/master/nsqadmin/nsqadmin.go">https://github.com/bitly/nsq/blob/master/nsqadmin/nsqadmin.go</a>)
func (n *NSQAdmin) Main() {
httpListener, err := net.Listen("tcp", n.httpAddr.String())
if err != nil {
n.logf("FATAL: listen (%s) failed - %s", n.httpAddr, err)
os.Exit(1)
}
n.httpListener = httpListener
httpServer := NewHTTPServer(&Context{n})
n.waitGroup.Wrap(func() {
util.HTTPServer(n.httpListener, httpServer, n.opts.Logger, "HTTP")
})
n.waitGroup.Wrap(func() { n.handleAdminActions() })
}
func (n *NSQAdmin) Exit() {
n.httpListener.Close()
close(n.notifications)
n.waitGroup.Wait()
}
在代码段2中,对waitGroup进行了简单封装,开启goroutines前计数加1,执行完计数减1。
代码段3中,Main()方法里,调用了两次waitGroup.Wrap()方法,参考代码段2可以知道,这会启动两个子goroutines,并使waitGroup计数加2.而子goroutines中使用了http包监听网络服务,阻塞goroutines,使得计数减1的操作不能被调用。
在我们的代码段1中可以知道,命令行发送了kill以后,会执行代码段3的Exit()方法,当方法里的n.httpListener.Close()被调用后,网络服务中断,代码段2中的阻塞就会停止,进而执行计数减1的,当两个子goroutines中计数各减1以后。Exit()方法中的n.waitGroup.Wait()就会继续执行,主线程结束,程序退出。
从nsq中学习如何优雅的退出go 网络程序的更多相关文章
- C# Note11:如何优雅地退出WPF应用程序
前言 I should know how I am supposed to exit my application when the user clicks on the Exit menu item ...
- 从别人的代码中学习golang系列--01
自己最近在思考一个问题,如何让自己的代码质量逐渐提高,于是想到整理这个系列,通过阅读别人的代码,从别人的代码中学习,来逐渐提高自己的代码质量.本篇是这个系列的第一篇,我也不知道自己会写多少篇,但是希望 ...
- 情景linux--如何优雅地退出telnet
情景linux--在脚本中如何优雅地退出telnet 情景 telnet命令是TELNET协议的用户接口,它支持两种模式:命令模式和会话模式.虽然telnet支持许多命令,但大部分情况下,我们只是使用 ...
- 在Object-C中学习数据结构与算法之排序算法
笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速 ...
- 如何优雅的退出/关闭/重启gunicorn进程
在工作中,会发现gunicorn启动的web服务,无论怎么使用kill -9 进程号都是无法杀死gunicorn,经过我一番百度和谷歌,发现想要删除gunicorn进程其实很简单. 1. 寻找mast ...
- hexo博客yili主题个性化自定义教程(1) ——借鉴中学习,初认yili主题
文章转载于:hexo博客yili主题个性化自定义教程(1) --借鉴中学习,初认yili主题 这个博客跌跌撞撞也弄了好多天了,由于Next主题不知道什么情况,被我玩坏了.所以换了一个主题. 大名鼎鼎的 ...
- 从别人的代码中学习golang系列--02
这篇博客还是整理从https://github.com/LyricTian/gin-admin 这个项目中学习的golang相关知识 作者在项目中使用了https://github.com/googl ...
- 【Golang】程序如何优雅的退出?
1. 背景 项目开发过程中,随着需求的迭代,代码的发布会频繁进行,在发布过程中,如何让程序做到优雅的退出? 为什么需要优雅的退出? 你的 http 服务,监听端口没有关闭,客户的请求发过来了,但处理了 ...
- jQuery中each的用法之退出循环和结束本次循环
jQuery中each的用法之退出循环和结束本次循环 jQuery中each类似于javascript的for循环 但不同于for循环的是在each里面不能使用break结束循环,也不能使用conti ...
随机推荐
- [kuangbin]树链剖分 C - Tree
和平常的树链剖分维护边权不同的地方在于对线段树的要求较高 NEGATE 反转区间,也就是a - b 内所有的边权取相反数 而Query询问是最大值,所以也就是维护可取反区间的最大值问题 需要维护的值是 ...
- poj2094
很不错的一道题,很让我见识到了差分序列的运用的神奇之处..一下是从北邮BBS看到的题解,写得很清楚..这边就直接转过来. uRLhttp://bbs.byr.cn/#!article/ACM_ICPC ...
- ASP.NET Web API 框架研究 Action方法介绍
在根据请求解析出匹配的Controller类型并创建实例后,要在该Controller类型中的众多Action方法中选择与请求匹配的那一个,并执行,然后返回响应. Action方法,其元数据,主要包括 ...
- 关于Servlet中GET和POST方法的总结
JSP.Servlet中get请求和post请求的区别总结 在学习JavaWeb最初的开始阶段,大家都会遇到HttpServlet中的doGet和doPost方法.关于Servlet中get请求和 ...
- 检查.net dll构建的目标平台是any cpu、x86、x64
有时候,需要检查构建的dll是否针对正确的平台 可以使用CorFlags.exe(它是.NET Framework SDK的一部分)从dll中查找此信息.运行CorFlags.exe将产生以下输出: ...
- 拖拽TreeViewItem到OCX控件
由于C#在性能方面,和C++还是有不少的差距,所以在项目中有一块是用C++的OCX控件实现,然后包括在WPF项目中.由于C++,C#属于不同的体系架构,造成了许多问题,特使是拖拽TreeViewIte ...
- 说一下acad的bug及问题
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk. ...
- .NET Core 常用加密和Hash工具NETCore.Encrypt
前言 在日常开发过程中,不可避免的涉及到数据加密解密(Hash)操作,所以就有想法开发通用工具,NETCore.Encrypt就诞生了.目前NETCore.Encrypt只支持.NET Core ,工 ...
- [Vuejs] 组件 v-if 和 v-show 切换时生命周期钩子的执行
v-if 初始渲染 初始值为 false 组件不会渲染,生命周期钩子不会执行,v-if 的渲染是惰性的. 初始值为 true 时,组件会进行渲染,并依次执行 beforeCreate,created, ...
- zookeeper单机版安装
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提供的功 ...