Golang应用程序在终端退出时如何发送邮件
Golang应用程序在终端退出时如何发送邮件 大家好,
我正在尝试编写一个代码,让我的Go语言应用程序使用“github.com/jordan-wright/email”这个库来发送电子邮件。
当运行该应用程序的终端或iTERM被关闭时,将发送邮件。
以下是我能够获取到的所有信息,并据此编写了以下代码。
每当终端或iTerm退出时,会发送一个SIGHUP信号,如果我们处理这个信号,就可以用它来执行一些操作。
package main
import (
"fmt"
"github.com/jordan-wright/email"
"io/ioutil"
"net/smtp"
"os"
"os/signal"
"syscall"
)
func main() {
c := make(chan os.Signal)
done := make(chan bool)
signal.Notify(c, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGHUP)
go handleInterrupts(c,done)
go execute1(done)
//performing some operation .......
}
func handleInterrupts(c chan os.Signal,done chan bool) {
//signal := <-c
signal := <-c
fmt.Println("Signal is :=", signal.String())
done <- true
}
func execute1(done chan bool) {
<- done
e := email.NewEmail()
e.From = "Harshit Singhvi <harshitsinghvi@XXXXX.com>"
e.To = []string{"harshit_singhvi@XXXX.com"}
e.Subject = "Test Email"
e.Text = []byte("Text Body is, of course, supported!")
e.AttachFile("files/files.go")
e.HTML = []byte("<h1>Demo</h1>")
auth := smtp.PlainAuth("", "XXXXXX", "XXXXX", "XXXXX")
e.Send("mail.XXXXX.com:25",auth)
ioutil.WriteFile("output.log", []byte("Hello"), 0644)
os.Exit(1)
}
当使用Ctrl + C中断程序时,邮件可以成功发送。
然而,每当终端关闭时,代码确实捕获到了SIGHUP信号,但邮件却从未发送出去。
请问有谁能告诉我,当运行上述代码的终端或iTerm被退出或突然终止时,我该如何发送电子邮件?
更多关于Golang应用程序在终端退出时如何发送邮件的实战教程也可以访问 https://www.itying.com/category-94-b0.html
是的,这是由系统决定的,而不是你的程序。
更多关于Golang应用程序在终端退出时如何发送邮件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
嗯。你尝试过在退出部分使用 defer 吗?
你尝试过使用 nohup、disown 或 screen 来运行你的应用吗?
因此,在这种情况下,无法控制运行Go程序的父程序(即终端)。
这只是我脑海中的一个问题,我正在尝试把它编码实现。
您可以监控进程的PID,但这更多是运维而非编程范畴的问题。
感谢您的回复 @NobbZ
我们能否在子进程终止前执行一段时间的操作。
我正在通过简单的 go run 命令运行我的应用程序。
go run main.go
你好,乔治,这里不能使用 monit,因为我只想在我的程序运行时终端被突然关闭的情况下发送邮件。
@samfrmd : 我想在终端被意外关闭时执行一个特定的操作。你认为 defer 在这里能起作用吗?
当父进程终止时,其子进程也会随之终止。某些终端模拟器可能会给 shell 一些时间进行清理关闭,而另一些则不会。
Monit 用于需要持续运行的应用。在这种情况下,应用仅在用户需要时才会执行,因此我认为无法为此类场景配置 Monit。
您的程序在持续运行时是存活的。一旦被系统终止,程序及其所有子进程都将被关闭,因此无法处理未来的函数。
为什么您需要那个程序在终端意外关闭时发送邮件?
为什么它不需要在正常关机或电源中断(或电源恢复)时发送邮件?
我不明白这样做有什么意义,如果终端关闭了,应用程序也会随之关闭。通过文件、进程ID或其他任何方式监控它们,都可以触发事件并发送邮件…
我的应用程序是一个菜单驱动的应用程序,需要在不同层级获取用户输入,因此我无法通过 nohup 将其作为后台进程运行。
是否有其他方法来处理父进程(在这种情况下是终端)的关闭?
不。
如果终端被突然终止,你的程序将如何被终止取决于你的终端,大多数终端会像执行 kill -9 $pid 一样杀死它们的子进程。你的程序没有机会处理这些事件。
// 代码示例:处理终端退出时的邮件发送逻辑
func handleTerminalExit() {
// 当运行Golang应用程序的终端退出时发送邮件
// 具体实现逻辑...
}
请了解 nohup、disown 或 screen 命令。这些命令可以确保你的应用程序在终端中按下 ctrl+c 后继续运行。
正如我上面所说,你可以使用专门为此设计的工具来监控你的应用程序。例如在Linux上,有一个名为monit的程序,它可以根据一组规则来启动和重启一个应用程序或进程,而无需考虑你是如何或从哪里启动的,或者进程为何关闭。你还可以在事件发生时发送电子邮件等等。但这属于应用程序管理范畴,而非编程问题。
package main
import (
"fmt"
"log"
"net/smtp"
"os"
"os/signal"
"syscall"
"time"
"github.com/jordan-wright/email"
)
func main() {
// 创建信号通道
sigChan := make(chan os.Signal, 1)
done := make(chan bool, 1)
// 注册需要捕获的信号
signal.Notify(sigChan,
syscall.SIGHUP, // 终端关闭
syscall.SIGINT, // Ctrl+C
syscall.SIGTERM, // 终止信号
syscall.SIGQUIT, // Ctrl+\
)
// 启动信号处理goroutine
go func() {
sig := <-sigChan
fmt.Printf("接收到信号: %v\n", sig)
// 发送邮件
if err := sendTerminationEmail(); err != nil {
log.Printf("发送邮件失败: %v", err)
} else {
fmt.Println("邮件发送成功")
}
done <- true
}()
// 模拟应用程序运行
fmt.Println("应用程序运行中...")
fmt.Println("尝试关闭终端或按Ctrl+C触发邮件发送")
// 等待信号
<-done
fmt.Println("应用程序退出")
}
func sendTerminationEmail() error {
// 创建邮件
e := email.NewEmail()
e.From = "应用程序 <sender@example.com>"
e.To = []string{"recipient@example.com"}
e.Subject = "应用程序终端关闭通知"
e.Text = []byte(fmt.Sprintf("应用程序于 %s 因终端关闭而退出", time.Now().Format(time.RFC3339)))
e.HTML = []byte(fmt.Sprintf(`
<h1>应用程序终止通知</h1>
<p>应用程序于 <strong>%s</strong> 因终端关闭而退出</p>
<p>请检查应用程序状态</p>
`, time.Now().Format(time.RFC3339)))
// 配置SMTP认证(根据实际情况修改)
auth := smtp.PlainAuth("", "username", "password", "smtp.example.com")
// 设置超时并发送邮件
return e.Send("smtp.example.com:587", auth)
}
对于终端关闭时邮件发送失败的问题,可以添加以下改进:
package main
import (
"context"
"fmt"
"log"
"net/smtp"
"os"
"os/signal"
"syscall"
"time"
"github.com/jordan-wright/email"
)
func main() {
// 使用带缓冲的通道防止阻塞
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
// 创建带超时的context
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 等待信号
go func() {
sig := <-sigChan
log.Printf("接收到终止信号: %v", sig)
// 在goroutine中发送邮件,避免阻塞信号处理
go func() {
if err := sendEmailWithRetry(ctx); err != nil {
log.Printf("最终发送邮件失败: %v", err)
}
os.Exit(0)
}()
// 给邮件发送留出时间
time.Sleep(2 * time.Second)
}()
// 主程序逻辑
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(1 * time.Second)
}
}
}
func sendEmailWithRetry(ctx context.Context) error {
maxRetries := 3
for i := 0; i < maxRetries; i++ {
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := sendEmail(); err == nil {
return nil
}
time.Sleep(time.Duration(i+1) * time.Second)
}
}
return fmt.Errorf("发送邮件重试%d次后失败", maxRetries)
}
func sendEmail() error {
e := email.NewEmail()
e.From = "监控系统 <monitor@example.com>"
e.To = []string{"admin@example.com"}
e.Subject = "终端关闭警报"
e.Text = []byte(fmt.Sprintf("终端于 %s 关闭", time.Now().Format("2006-01-02 15:04:05")))
auth := smtp.PlainAuth("", "user", "pass", "smtp.example.com")
return e.Send("smtp.example.com:587", auth)
}
关键改进点:
- 使用带缓冲的信号通道
- 添加超时控制
- 实现重试机制
- 在独立的goroutine中发送邮件避免阻塞
- 确保有足够的时间完成邮件发送

