我是 golang 新手。 我有一个 go 程序,它是一个 for 循环,就像
for {
f()
}
f
可能需要几秒钟到 1 分钟。 for 循环永远运行,并且可以被 KILL 信号终止。
在 k8s 容器中,我们可能会经常杀死程序/进程。所以我希望 KILL 信号仅在函数
f
完成且未启动后中断 for 循环。
如何做到这一点?有例子吗?
谢谢
要对信号执行某些操作,您将需要一些侦听信号的代码。请注意,您无法监听 SIGKILL,但这不是您的应用程序收到的信号(除非使用类似
kill $PID
的信号)。在大多数情况下,您的应用程序将收到 SIGTERM。
最简单的聆听方法如下:
quit := make(chan os.Signal, 1) // Create a channel for the signal
signal.Notify(quit, os.Interrupt, syscall.SIGTERM) // Instruct the runtime to send signals to that channel
<-quit // Read from the channel. This operation blocks until there is something to read
select
来实现这一点。 select
与 switch
类似,不同之处在于它会检查每个 case
是否有“那里还有东西”;如果没有任何一种情况,则 default
被执行。非常完美:您可以使用 case
检查是否收到信号,否则使用 default
执行您的函数。
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
for {
select {
case <-quit:
return
default:
f()
}
}
对于小型、单一用途的应用程序,上述内容可能已经足够了。
对于较大的应用程序,通常创建一个“根上下文”,使用
context.WithCancel()
包装它,将该 ctx 传递到所有 goroutine 中。每个 goroutine 不是直接监听信号,而是检查 <-ctx.Done()
。主 Goroutine(或单独的专用 Goroutine)侦听操作系统信号并调用 context.WithCancel()
返回的取消函数。
后来的设置不仅更具可扩展性,而且还可以与外部代码很好地配合使用;例如,如果应用程序正在关闭,
database/sql.DB.QueryContext()
会执行相同的技巧检查<-ctx.Done()
以停止正在执行的操作。