您的位置:首页 >聚焦 >

GO的优雅终止姿势

2022-02-28 19:03:37    来源:程序员客栈

最近优化了一版程序:用到了golang的优雅退出机制。

程序使用etcd的election sdk做高可用选主,需要在节点意外下线的时候,主动去etcd卸任(删除10s租约), 否则已经下线的节点还会被etcd认为是leader。

所以在这里,优雅退出是技术刚需。

另外根据《云原生十二要素方法论》第9条:快速启动和优雅终止可最大化健壮性, 也推荐各位遵守实践。Fast startup and shutdown are advocated for a more robust and resilient system.

粗浅的认知方案:捕获程序的终止信号, 主动去卸任。

标准信号[1]Linux支持如下标准信号,第二列指示该信号遵守的标准。

SignalStandardActionComment────────────────────────────────────────────────────────────────────────SIGABRTP1990CoreAbortsignalfromabort(3)SIGALRMP1990TermTimersignalfromalarm(2)SIGBUSP2001CoreBuserror(badmemoryaccess)SIGCHLDP1990IgnChildstoppedorterminatedSIGCLD-IgnAsynonymforSIGCHLDSIGCONTP1990ContContinueifstoppedSIGEMT-TermEmulatortrapSIGFPEP1990CoreFloating-pointexceptionSIGHUPP1990TermHangupdetectedoncontrollingterminalordeathofcontrollingprocessSIGILLP1990CoreIllegalInstructionSIGINFO-AsynonymforSIGPWRSIGINTP1990TermInterruptfromkeyboardSIGIO-TermI/Onowpossible(4.2BSD)SIGIOT-CoreIOTtrap.AsynonymforSIGABRTSIGKILLP1990TermKillsignalSIGLOST-TermFilelocklost(unused)SIGPIPEP1990TermBrokenpipe:writetopipewithnoreaders;seepipe(7)SIGPOLLP2001TermPollableevent(SysV);synonymforSIGIOSIGPROFP2001TermProfilingtimerexpiredSIGPWR-TermPowerfailure(SystemV)SIGQUITP1990CoreQuitfromkeyboardSIGSEGVP1990CoreInvalidmemoryreferenceSIGSTKFLT-TermStackfaultoncoprocessor(unused)SIGSTOPP1990StopStopprocessSIGTSTPP1990StopStoptypedatterminalSIGSYSP2001CoreBadsystemcall(SVr4);seealsoseccomp(2)SIGTERMP1990TermTerminationsignalSIGTRAPP2001CoreTrace/breakpointtrapSIGTTINP1990StopTerminalinputforbackgroundprocessSIGTTOUP1990StopTerminaloutputforbackgroundprocessSIGUNUSED-CoreSynonymouswithSIGSYSSIGURGP2001IgnUrgentconditiononsocket(4.2BSD)SIGUSR1P1990TermUser-definedsignal1SIGUSR2P1990TermUser-definedsignal2SIGVTALRMP2001TermVirtualalarmclock(4.2BSD)SIGXCPUP2001CoreCPUtimelimitexceeded(4.2BSD);seesetrlimit(2)SIGXFSZP2001CoreFilesizelimitexceeded(4.2BSD);seesetrlimit(2)SIGWINCH-IgnWindowresizesignal(4.3BSD,Sun)

其中SIGKILL,SIGSTOP信号不能被捕获、阻塞、忽略。

我们常见的三种终止程序的操作:

1.CTRL+C实际是发送SIGINT信号,2.kill pid的作用是向指定进程发送SIGTERM信号(这是kill默认发送的信息), 若应用程序没有捕获并响应该信号的逻辑,则该信号默认动作是kill掉进程,这是终止进程的推荐做法。3.kill -9 pid则是向指定进程发送SIGKILL信号,SIGKILL信号既不能被应用程序捕获,也不能被阻塞或忽略,

故要达成我们的目的,这里捕获SIGINTSIGTREM信号就可满足需求。


golang提供signal包来监听并反馈收到的信号。

可针对长时间运行的程序,新开协程,持续监听信号,并插入优雅关闭的代码。

c:=make(chanos.Signal)signal.Notify(c,syscall.SIGTERM,syscall.SIGINT)gofunc(){select{casesig:=<-c:{log.Infof("Got%ssignal.Aborting...\n",sig)eCli.Close()//利用etcdelectionsdk主动卸任os.Exit(1)}}}()

是不是依旧适配容器?

我们得看DOCKER官方docker stop,docker kill命令的定义。

docker stop[2]: The main process inside the container will receiver SIGTREM, and after a grace period,SIGKILL .(default grace period =10s)

docker kill[3]:The main process inside the container is sent SIGKILL signal (default), or the signal that is specified with the --signal option

我们常用的docker stop命令:向容器内进程发送SIGTREM信号,10s后发送SIGKILL信号,这10s时间给了程序做优雅关闭的时机,所以上面代码的逻辑是能适配容器的。

Ref:十二要素App方法论

引用链接

[1]标准信号:https://www.man7.org/linux/man-pages/man7/signal.7.html[2]docker stop:https://docs.docker.com/engine/reference/commandline/stop/[3]docker kill:https://docs.docker.com/engine/reference/commandline/kill/

有态度的马甲建立了真●高质量交流群:大佬汇聚、无事静默、有事激活、深度思考。

[长按图片加我好友]

年终总结:2021技术文大盘点 | 打包过去,面向未来

项目总结:麻雀虽小,五脏俱全

理念总结:实话实说:只会.NET,会让我们一直处于鄙视链、食物链的下游

云原生系列:什么是云原生?

点“赞”戳“在看”

体现态度很有必要!

关键词: 标准信号 应用程序 年终总结

相关阅读