Golang中如何检测和处理故障

Golang中如何检测和处理故障 我正在开发一个基于REST API的SaaS应用,该应用使用Golang构建并部署在EC2实例上。在这个产品中,我设置了用于不同目的的定时任务,比如向客户发送提醒和标记预约完成。我使用了gin-gonic框架。大多数情况下定时任务运行正常,但有时会不执行。我不确定如何排查这个问题。最初我以为服务器停止了,但检查后发现它仍在8080端口运行,只是定时任务没有执行。

有没有方法可以追踪故障并解决它们?我也愿意接受关于定时任务实现的建议,因为我相当确定自己实现得不够好。以下是我的实现:

package main
import (
    "bkapi/cron"
)
func main(){
    cron.RunCron()
    NewRouter()
}

以下是我的定时任务包:

package cron
import (
    "gopkg.in/robfig/cron.v2"
    "bkapi/utils"
    "bkapi/config"
)
func RunCron() {
    c := cron.New()
    c.AddFunc("@every 0h15m0s", SendOneDayReminders)
    c.Start()
}
func SendOneDayReminders(){
    utils.ExecuteCommand("sh "+config.GetBasePath()+"sh/one_day_booking_reminders.sh")
}

每个定时任务都在执行一个sh文件,其中包含所有商户的URL,例如:

curl "127.0.0.1:8080/ren/complete-booking"

这是在EC2实例上本地运行API的。


更多关于Golang中如何检测和处理故障的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中如何检测和处理故障的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中检测和处理定时任务故障需要系统性的方法。以下是针对您问题的具体解决方案和代码示例:

1. 增强定时任务的错误处理和日志记录

package cron

import (
    "log"
    "os"
    "time"
    "gopkg.in/robfig/cron.v2"
    "bkapi/utils"
    "bkapi/config"
)

var logger = log.New(os.Stdout, "CRON: ", log.Ldate|log.Ltime|log.Lshortfile)

func RunCron() {
    c := cron.New()
    
    // 添加错误处理中间件
    c.AddFunc("@every 0h15m0s", withErrorHandling(SendOneDayReminders))
    
    c.Start()
    logger.Println("定时任务已启动")
}

// 错误处理包装器
func withErrorHandling(f func()) func() {
    return func() {
        defer func() {
            if r := recover(); r != nil {
                logger.Printf("定时任务发生panic: %v", r)
            }
        }()
        
        start := time.Now()
        logger.Printf("开始执行定时任务")
        
        f()
        
        duration := time.Since(start)
        logger.Printf("定时任务执行完成,耗时: %v", duration)
    }
}

func SendOneDayReminders() {
    logger.Printf("执行一天前提醒任务")
    
    cmd := "sh " + config.GetBasePath() + "sh/one_day_booking_reminders.sh"
    output, err := utils.ExecuteCommandWithOutput(cmd)
    if err != nil {
        logger.Printf("执行命令失败: %v, 输出: %s", err, output)
        return
    }
    
    logger.Printf("命令执行成功,输出: %s", output)
}

2. 改进命令执行工具函数

// 在 utils 包中添加带输出的命令执行函数
package utils

import (
    "bytes"
    "log"
    "os/exec"
)

func ExecuteCommandWithOutput(command string) (string, error) {
    cmd := exec.Command("sh", "-c", command)
    
    var stdout, stderr bytes.Buffer
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr
    
    err := cmd.Run()
    
    output := stdout.String()
    if err != nil {
        errorOutput := stderr.String()
        log.Printf("命令执行错误: %v, stderr: %s", err, errorOutput)
        return output, err
    }
    
    return output, nil
}

3. 添加健康检查端点

// 在您的 gin 路由中添加健康检查
package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

func NewRouter() *gin.Engine {
    router := gin.Default()
    
    // 健康检查端点
    router.GET("/health", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "status":    "healthy",
            "timestamp": time.Now().Unix(),
            "service":   "booking-api",
        })
    })
    
    // 定时任务状态端点
    router.GET("/cron-status", func(c *gin.Context) {
        // 这里可以返回定时任务的执行状态
        c.JSON(http.StatusOK, gin.H{
            "last_execution": "需要实现状态跟踪",
            "next_execution": "需要实现状态跟踪",
        })
    })
    
    return router
}

4. 使用更可靠的定时任务库

考虑使用 github.com/robfig/cron/v3 替代旧版本:

package cron

import (
    "context"
    "log"
    "time"
    "github.com/robfig/cron/v3"
)

func RunCron() {
    c := cron.New(cron.WithLogger(
        cron.VerbosePrintfLogger(log.New(log.Writer(), "CRON: ", log.LstdFlags)),
    ))
    
    // 使用更精确的调度表达式
    _, err := c.AddFunc("*/15 * * * *", withErrorHandling(SendOneDayReminders))
    if err != nil {
        log.Printf("添加定时任务失败: %v", err)
        return
    }
    
    // 添加上下文支持以便优雅关闭
    ctx := context.Background()
    c.Start()
    
    // 保持主goroutine运行
    select {
    case <-ctx.Done():
        c.Stop()
    }
}

5. 实现任务状态追踪

package cron

import (
    "sync"
    "time"
)

type TaskStatus struct {
    LastExecution time.Time
    LastError     string
    SuccessCount  int
    ErrorCount    int
    mu            sync.RWMutex
}

var taskStatus = make(map[string]*TaskStatus)

func recordTaskExecution(taskName string, success bool, errMsg string) {
    if _, exists := taskStatus[taskName]; !exists {
        taskStatus[taskName] = &TaskStatus{}
    }
    
    taskStatus[taskName].mu.Lock()
    defer taskStatus[taskName].mu.Unlock()
    
    taskStatus[taskName].LastExecution = time.Now()
    if success {
        taskStatus[taskName].SuccessCount++
    } else {
        taskStatus[taskName].ErrorCount++
        taskStatus[taskName].LastError = errMsg
    }
}

这些改进将帮助您更好地检测和处理定时任务故障,并提供详细的日志用于问题排查。

回到顶部