Golang代码开发经验分享
Golang代码开发经验分享 你好,
我急需一位专家提供帮助。下面这个问题让我备受困扰。我正在尝试构建一个自动邮件发送器。除了自动发送邮件外,它还必须能够发送定时邮件。
请帮帮我。
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"time"
"gopkg.in/gomail.v2"
)
type Config struct {
SMTPServer string `json:"smtp_server"`
SMTPPort int `json:"smtp_port"`
SMTPUser string `json:"smtp_user"`
SMTPPassword string `json:"smtp_password"`
EmailFrom string `json:"email_from"`
EmailTo string `json:"email_to"`
}
func main() {
// 解析命令行参数
scheduleDate := flag.String("date", "", "Date for sending the email (YYYY-MM-DD)")
scheduleTime := flag.String("time", "", "Time for sending the email (HH:MM)")
flag.Parse()
// 验证命令行参数
if *scheduleDate == "" || *scheduleTime == "" {
fmt.Println("Please provide both the date and time for scheduling the email.")
return
}
// 打开 JSON 文件
file, err := os.Open("config.json")
if err != nil {
panic(err)
}
defer file.Close()
// 读取 JSON 文件
bytes, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
// 将 JSON 数据解码到 Config 结构体中
var config Config
err = json.Unmarshal(bytes, &config)
if err != nil {
panic(err)
}
// 创建新的邮件消息
m := gomail.NewMessage()
// 设置邮件详情
m.SetHeader("From", config.EmailFrom)
m.SetHeader("To", config.EmailTo)
m.SetHeader("Subject", "Test Mail from Go")
// 设置邮件正文
m.SetBody("text/html", "This is the content of the email.")
// 为邮件服务器创建新的拨号器
d := gomail.NewDialer(config.SMTPServer, config.SMTPPort, config.SMTPUser, config.SMTPPassword)
// 解析提供的日期和时间字符串
layout := "2006-01-02 15:04" // 用于解析日期和时间的格式
scheduleDateTime := fmt.Sprintf("%s %s", *scheduleDate, *scheduleTime)
scheduledTime, err := time.Parse(layout, scheduleDateTime)
if err != nil {
fmt.Println("Invalid date or time format. Please provide the date in the format 'YYYY-MM-DD' and the time in the format 'HH:MM'.")
return
}
// 计算到预定时间的持续时间
duration := scheduledTime.Sub(time.Now())
// 检查预定时间是否已过
if duration < 0 {
fmt.Println("The specified scheduled time has already passed.")
return
}
// 打印预定时间用于调试
fmt.Println("Scheduled time:", scheduledTime)
// 安排在指定时间发送邮件
timer := time.NewTimer(duration)
// 等待计时器到期
<-timer.C
// 打印当前时间用于调试
fmt.Println("Current time:", time.Now())
// ...
// 使用拨号器发送邮件
if err := d.DialAndSend(m); err != nil {
fmt.Println("Failed to send the email:", err)
os.Exit(1)
}
// 打印邮件已发送的消息
fmt.Println("The email has been sent!")
}
更多关于Golang代码开发经验分享的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
你好,@Huseyin_Genc,你具体遇到了什么问题?
更多关于Golang代码开发经验分享的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个很好的定时邮件发送器基础实现。我来分享一些关键的Go语言开发经验,特别是针对你的代码可以改进的地方:
1. 使用context处理超时和取消
// 在main函数中添加context支持
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 修改发送邮件部分支持context
if err := d.DialAndSendContext(ctx, m); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("Email sending timed out")
} else {
fmt.Println("Failed to send the email:", err)
}
os.Exit(1)
}
2. 改进配置管理
// 使用结构体标签验证
type Config struct {
SMTPServer string `json:"smtp_server" validate:"required,hostname_port"`
SMTPPort int `json:"smtp_port" validate:"required,min=1,max=65535"`
SMTPUser string `json:"smtp_user" validate:"required,email"`
SMTPPassword string `json:"smtp_password" validate:"required"`
EmailFrom string `json:"email_from" validate:"required,email"`
EmailTo string `json:"email_to" validate:"required,email"`
}
// 使用go-playground/validator进行验证
func loadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read config file: %w", err)
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("unmarshal config: %w", err)
}
validate := validator.New()
if err := validate.Struct(config); err != nil {
return nil, fmt.Errorf("validate config: %w", err)
}
return &config, nil
}
3. 实现邮件模板支持
// 邮件模板结构
type EmailTemplate struct {
Subject string `json:"subject"`
Body string `json:"body"`
}
// 模板渲染函数
func renderTemplate(templatePath string, data interface{}) (string, string, error) {
tmpl, err := template.ParseFiles(templatePath)
if err != nil {
return "", "", err
}
var subjectBuf, bodyBuf bytes.Buffer
if err := tmpl.ExecuteTemplate(&subjectBuf, "subject", data); err != nil {
return "", "", err
}
if err := tmpl.ExecuteTemplate(&bodyBuf, "body", data); err != nil {
return "", "", err
}
return subjectBuf.String(), bodyBuf.String(), nil
}
// 在main中使用
subject, body, err := renderTemplate("email.tmpl", map[string]interface{}{
"UserName": "John Doe",
"SendTime": time.Now().Format(time.RFC1123),
})
if err != nil {
log.Fatal("Failed to render template:", err)
}
m.SetHeader("Subject", subject)
m.SetBody("text/html", body)
4. 添加重试机制
// 带指数退避的重试发送
func sendWithRetry(d *gomail.Dialer, m *gomail.Message, maxRetries int) error {
var lastErr error
for i := 0; i < maxRetries; i++ {
if i > 0 {
// 指数退避
backoff := time.Duration(math.Pow(2, float64(i))) * time.Second
time.Sleep(backoff)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := d.DialAndSendContext(ctx, m); err != nil {
lastErr = err
log.Printf("Attempt %d failed: %v", i+1, err)
continue
}
return nil
}
return fmt.Errorf("failed after %d attempts: %w", maxRetries, lastErr)
}
// 使用重试发送
if err := sendWithRetry(d, m, 3); err != nil {
fmt.Println("Failed to send email after retries:", err)
os.Exit(1)
}
5. 改进定时调度
// 使用cron表达式支持复杂调度
import "github.com/robfig/cron/v3"
func scheduleEmail(cronExpr string, config Config) error {
c := cron.New()
_, err := c.AddFunc(cronExpr, func() {
// 发送邮件逻辑
m := gomail.NewMessage()
m.SetHeader("From", config.EmailFrom)
m.SetHeader("To", config.EmailTo)
m.SetHeader("Subject", "Scheduled Email")
m.SetBody("text/html", "This is a scheduled email.")
d := gomail.NewDialer(config.SMTPServer, config.SMTPPort,
config.SMTPUser, config.SMTPPassword)
if err := d.DialAndSend(m); err != nil {
log.Printf("Failed to send scheduled email: %v", err)
} else {
log.Println("Scheduled email sent successfully")
}
})
if err != nil {
return fmt.Errorf("invalid cron expression: %w", err)
}
c.Start()
return nil
}
6. 添加并发安全性和优雅关闭
// 使用sync.WaitGroup管理goroutine
var wg sync.WaitGroup
stopChan := make(chan struct{})
// 信号处理
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
// 启动邮件发送goroutine
wg.Add(1)
go func() {
defer wg.Done()
select {
case <-timer.C:
// 发送邮件逻辑
if err := d.DialAndSend(m); err != nil {
log.Printf("Failed to send email: %v", err)
}
case <-stopChan:
log.Println("Email sending cancelled")
return
}
}()
// 等待信号
<-signalChan
close(stopChan)
// 等待所有goroutine完成
wg.Wait()
fmt.Println("Shutdown complete")
7. 添加日志记录
// 结构化日志
import "go.uber.org/zap"
func initLogger() *zap.Logger {
logger, err := zap.NewProduction()
if err != nil {
log.Fatal("Failed to create logger:", err)
}
return logger
}
// 在main中使用
logger := initLogger()
defer logger.Sync()
logger.Info("Starting email scheduler",
zap.String("scheduled_time", scheduleDateTime),
zap.String("from", config.EmailFrom),
zap.String("to", config.EmailTo),
)
// 记录发送结果
if err := d.DialAndSend(m); err != nil {
logger.Error("Failed to send email",
zap.Error(err),
zap.String("to", config.EmailTo),
)
os.Exit(1)
}
logger.Info("Email sent successfully",
zap.String("to", config.EmailTo),
zap.Time("sent_at", time.Now()),
)
这些改进可以让你的邮件发送器更加健壮、可维护和可扩展。关键点包括:错误处理、配置验证、模板支持、重试机制、更好的调度支持和结构化日志。

