golang 后台进程的启动和停止操作
来源:脚本之家
时间:2023-01-07 12:10:21 409浏览 收藏
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《golang 后台进程的启动和停止操作》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
启动命令
我们先来个非后台运行的启动命令
func init() { startCmd := &cobra.Command{ Use: "start", Short: "Start Gonne", Run: func(cmd *cobra.Command, args []string) { startHttp() }, } startCmd.Flags().BoolVarP(&daemon, "deamon", "d", false, "is daemon?") RootCmd.AddCommand(startCmd) }
startHttp方法启动一个http的web服务
func startHttp() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello cmd!") }) if err := http.ListenAndServe(":9090", nil); err != nil { log.Fatal("ListenAndServe: ", err) } }
现在通过gonne start便可以启动一个web服务了,但是程序停留在命令行,如果ctrl+C程序也会终止了
命令行参数
如果想要后台启动,那么得让start命令知道是要后台运行的,参照docker命令行的方式就是加上-d,给一个命令添加参数的判断只需很少的代码
改造一下代码
func init() { var daemon bool startCmd := &cobra.Command{ Use: "start", Short: "Start Gonne", Run: func(cmd *cobra.Command, args []string) { if daemon { fmt.Println("gonne start",daemon) } startHttp() }, } startCmd.Flags().BoolVarP(&daemon, "deamon", "d", false, "is daemon?") RootCmd.AddCommand(startCmd) }
命令行输入
gonne start -d
这样就可以接收到-d参数了,这里要说明一下,第一个参数取值,第二个参数代码--deamon,第三个参数代表-d
第四个参数代码不加-d时候的默认值,第五参数是描述
后台运行
后台运行其实这里使用的是一个巧妙的方法,就是使用系统的command命令行启动自己的命令行输入,是不是有点绕,再看看看改造后的代码
Run: func(cmd *cobra.Command, args []string) { if daemon { command := exec.Command("gonne", "start") command.Start() fmt.Printf("gonne start, [PID] %d running...\n", command.Process.Pid) ioutil.WriteFile("gonne.lock", []byte(fmt.Sprintf("%d", command.Process.Pid)), 0666) daemon = false os.Exit(0) } else { fmt.Println("gonne start") } startHttp() },
用exec的Command启动刚输入的gonne start -d,就会拦截到这条请求然后通过gonne start,但是程序就不会停留在命令行了,然后发现http服务还在,还可以访问。
还有一点就是把pid输出到gonne.lock文件,给停止的程序调用
终止后台程序
有了之前的操作后,停止就简单多了
func init() { RootCmd.AddCommand(stopCmd) } var stopCmd = &cobra.Command{ Use: "stop", Short: "Stop Gonne", Run: func(cmd *cobra.Command, args []string) { strb, _ := ioutil.ReadFile("gonne.lock") command := exec.Command("kill", string(strb)) command.Start() println("gonne stop") }, }
执行 gonne stop 即可终止之前启动的http服务
help命令
好了,关于命令的操作讲完了,再看看cobra给的福利,自动生成的help命令
这个不需要你做什么操作,只需要输入gonne help,相关信息已经帮你生产好了。
appletekiMacBook-Pro:andev apple$ gonne help Usage: gonne [flags] gonne [command] Available Commands: help Help about any command start Start Gonne stop Stop Gonne version Print the version number of Gonne Flags: -h, --help help for gonne Use "gonne [command] --help" for more information about a command.
当然,子命令也有
appletekiMacBook-Pro:andev apple$ gonne start -h Start Gonne Usage: gonne start [flags] Flags: -d, --deamon is daemon? -h, --help help for start
自此告别各种脚本!
补充:golang子进程的启动和停止,mac与linux的区别
今天接到一个任务是将原来运行在mac的应用移植到linux,原因当然是因为客户那边当前是linux环境,也不想再采购mac电脑。
通常来说,这个工作并不难,因为我选用的服务器端技术是c或者golang,这两种技术具有很好的可移植性,而且大多是重新编译即可运行,所以接到任务的开始并没有把这个当一回事。
跟想象中的也差不多,搭建好linux测试服务器,在mac上把运行很久的应用重新交叉编译了一遍,部署到linux实验环境,启动、测试,看起来一切正常。准备打包交活,这时候发现一个问题,程序无法终止。
简单调试后就找到了原因,在系统中启动的子进程,发出终止信号之后居然仍在运行,导致父进程也一直无法退出,尴尬了。
列一下采用的代码(代码为简化版仅供示例):
func startChild1() { cmd := exec.Command("/bin/sh", "-c", "sleep 1000") time.AfterFunc(10*time.Second, func() { fmt.Println("PID1=", cmd.Process.Pid) syscall.Kill(-cmd.Process.Pid, syscall.SIGQUIT) fmt.Println("killed") }) fmt.Println("begin run") cmd.Run() }
示例代码首先启动一个sleep的子进程,表示某个子业务开始工作,然后延时10秒钟之后,把这个子进程杀死。
这段代码启动子进程和关闭子进程在mac电脑的原有系统上工作都很正常,但是到了linux,启动子进程仍然没有问题,关闭子进程不成功。
检查了一下在linux的工作过程,发现启动子进程之后,实际上是启动了两个进程,一个进程是/bin/sh,随后sh又启动了一个子进程自身的子进程sleep。
而发出退出命令的时候,只有sh退出了,sleep进程仍然继续运行。对比同样的mac电脑上,sh进程是没有出现的,只有一个sleep进程,所以发出退出命令的时候,sleep正常关闭,系统表现正常。
使用/bin/sh来启动另外的命令行程序是有原因的,这源于golang本身的设计,golang的exec.Command,后面第一个参数是命令行程序本身,之后的每一个exec.Command参数,都代表命令行程序的一个参数,而不是我们常用的,命令行程序路径和参数都可以写在一个字符串,用空格隔开即可。
所以有的时候我们是为了省事,也有的时候是顺手移植了别的语言的代码,就使用/bin/sh来启动需要的命令行程序,就如同上面示例代码一样,这样情况下,除了-c参数要单独占用一个字符串,我们原本要启动的字符串程序及其参数,就可以如同常见语言处理方式那样,放在一个字符串了。
我们可以尝试一下这个代码:
func startChild2() { cmd := exec.Command("sleep", "1000") time.AfterFunc(10*time.Second, func() { fmt.Println("PID2=", cmd.Process.Pid) syscall.Kill(-cmd.Process.Pid, syscall.SIGQUIT) fmt.Println("killed") }) fmt.Println("begin run") cmd.Run() }
测试一下,这段代码因为没有经过/bin/sh程序,在linux上也只有sleep这一个进程被建立,直接向其发出退出指令是可以正常工作的。这从进程的观察中及实验的结果中,都可以证实我们的判断。
知道了原因,处理起来也很容易,一是把程序改成类似上面这样的方式启动进程。另外一个办法则是直接为/bin/sh及我们的命令行进程建立一个进程组,这样最后发出的指令退出这个进程组,同样可以同时退出/bin/sh及sleep两个进程,达到我们的需求。
写代码测试一下:
func startChild3() { cmd := exec.Command("/bin/sh", "-c", "sleep 1000") cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} time.AfterFunc(10*time.Second, func() { fmt.Println("PID3=", cmd.Process.Pid) syscall.Kill(-cmd.Process.Pid, syscall.SIGQUIT) fmt.Println("killed") }) fmt.Println("begin run") cmd.Run() }
经过实际测试,这段代码在不改变原有的命令行参数传递习惯的基础上,可以正常在linux及mac电脑顺利执行。
最后再说一下命令cmd.Process.Signal,golang文档上说的很清楚,这是向进程发送消息信号,比如同样的syscall.SIGQUIT,这也是告诉子进程退出的意思。
所以大多的应用中,我们希望一个进程退出,直接用:
cmd.Process.Signal(syscall.SIGQUIT)
也是可以正常执行的,但对于我们上面说的情况,如果先使用/bin/sh启动了另外一个子进程,这种方法就无效了(指在linux无效,mac测试是一样可以用的,关键区别同样是在mac,/bin/sh进程不会保留并等待我们启动的子进程退出,所以退出消息可以正常的发送到正常的子进程)。
所以为了跨平台的通用性,建议还是使用Process.Kill或者syscall.Kill来杀死子进程。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持golang学习网。如有错误或未考虑完全的地方,望不吝赐教。
到这里,我们也就讲完了《golang 后台进程的启动和停止操作》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于golang的知识点!
-
160 收藏
-
105 收藏
-
106 收藏
-
121 收藏
-
404 收藏
-
384 收藏
-
246 收藏
-
110 收藏
-
210 收藏
-
108 收藏
-
367 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 结实的芝麻
- 这篇文章内容出现的刚刚好,作者加油!
- 2023-05-29 14:40:16
-
- 大方的火龙果
- 这篇博文真是及时雨啊,太细致了,太给力了,码起来,关注作者大大了!希望作者大大能多写Golang相关的文章。
- 2023-04-23 01:57:09
-
- 糟糕的黑裤
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢老哥分享技术贴!
- 2023-03-17 04:33:53
-
- 典雅的鲜花
- 很详细,mark,感谢博主的这篇技术文章,我会继续支持!
- 2023-03-04 10:55:53
-
- 凶狠的海燕
- 这篇文章内容真是及时雨啊,细节满满,很好,收藏了,关注博主了!希望博主能多写Golang相关的文章。
- 2023-01-10 01:03:50