从nsq中学习如何优雅的退出go 网络程序
来源:互联网 发布:手机模板源码 编辑:程序博客网 时间:2024/06/07 16:52
退出运行中的程序,可以粗暴的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):
2
3
4
5
6
7
8
9
10
11
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()实现优雅退出。当然上面的代码,也可以简化:
2
3
4
5
6
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)
2
3
4
5
6
7
8
9
10
11
sync.WaitGroup
}
func (w *WaitGroupWrapper) Wrap(cb func()){
w.Add(1)
go func() {
cb()
w.Done()
}()
}
代码段3(来自:https://github.com/bitly/nsq/blob/master/nsqadmin/nsqadmin.go)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 网络程序
- Android中优雅的退出程序
- Android中优雅的退出程序
- 在go语言中优雅退出goroutines
- 安卓开发如何优雅的实现退出整个程序
- Go 语言中实现优雅的停止程序
- 如何优雅的退出goroutine
- 如何在go程序中捕获退出信号
- 如何优雅地退出python程序
- 网络编程 - boost::asio优雅的退出
- go语言之 panic, recover ——如何在go语言中优雅的处理错误
- boost asio程序优雅的退出 一
- boost asio程序优雅的退出 二
- 优雅的退出程序---->持续更新
- Go程序如何安全退出(CTRL+C)
- 如何优雅的退出android应用
- android——如何从一个activity中退出程序
- 【Linux学习笔记】kill及kill -9的用法及如何实现进程的优雅退出
- git命令之git fetch的用法
- 利用channel在goroutins之间控制同步和传递数据
- 【OpenCV学习笔记 008】基于形态学运算的图像变换
- LeetCode练习-难题卷
- 我的ubuntu问题汇总
- 从nsq中学习如何优雅的退出go 网络程序
- [阶段二]Android UI窗口组件
- Deepin logo国内 Linux 发行版 Deepin
- Go原子计数
- spring加载--从xml配置文件到内存
- Ubuntu软件安装
- Docker快速入门
- [CORS:跨域资源共享] ASP.NET Web API自身对CORS的支持: CORS授权检验的实施
- [阶段二]Android UI列表组件