为了账号安全,请及时绑定邮箱和手机立即绑定

继续在 Golang 中重试函数

继续在 Golang 中重试函数

Go
茅侃侃 2022-08-24 16:18:34
我正在尝试创建一个功能,该功能将按以下方式工作:调用服务函数后,它使用 Fetch 函数从服务(以字节数组的形式出现)获取记录,JSON 取消元帅字节数组,填充结构,然后将结构发送到数据库函数以保存到数据库。现在,由于这需要是一个连续的作业,因此我添加了两个 if 条件,如果收到的记录长度为 0,则我们使用重试函数重试拉取记录,否则我们只需写入数据库。我一直在尝试调试重试函数一段时间,但它只是不起作用,并且在第一次重试后基本上停止了(即使我将尝试指定为100)。我能做些什么来确保,它不断重试拉动记录?代码如下:// RETRY FUNCTIONfunc retry(attempts int, sleep time.Duration, f func() error) (err error) {for i := 0; ; i++ {    err = f()    if err == nil {        return    }    if i >= (attempts - 1) {        break    }    time.Sleep(sleep)    sleep *= 2    log.Println("retrying after error:", err)}return fmt.Errorf("after %d attempts, last error: %s", attempts, err) }//Save Data function type Records struct {Messages [][]byte}func (s *Service) SaveData(records Records, lastSentPlace uint) error {//lastSentPlace is sent as 0 to begin with.for i := lastSentPlace; i <= records.Place-1; i++ {    var msg Records    msg.Unmarshal(records.Messages[i])    order := MyStruct{        Fruit:    msg.Fruit,        Burger:   msg.Burger,        Fries:    msg.Fries,     }    err := s.db.UpdateOrder(context.TODO(), nil , order)    if err != nil {        logging.Error("Error occured...")    }    }return nil}//Service function (This runs as a batch, which is why we need retrying)func (s *Service) MyServiceFunction(ctx context.Context, place uint, length uint) (err error) {var lastSentPlace = placerecords, err := s.Poll(context.Background(), place, length)if err != nil {    logging.Info(err)}我认为,问题在于我尝试实现重试函数的方式,我已经尝试调试了一段时间,但是作为该语言的新手,我真的卡住了。我想做的是,如果没有找到任何记录,就实现回退。任何帮助都非常感谢。
查看完整描述

4 回答

?
偶然的你

TA贡献1841条经验 获得超3个赞

我进行更简单的重试。

  • 使用更简单的循环逻辑来确保正确性。

  • 我们在执行重试之前会睡觉,因此请用作睡眠的条件。i > 0

代码如下:

func retry(attempts int, sleep time.Duration, f func() error) (err error) {

    for i := 0; i < attempts; i++ {

        if i > 0 {

            log.Println("retrying after error:", err)

            time.Sleep(sleep)

            sleep *= 2

        }

        err = f()

        if err == nil {

            return nil

        }

    }

    return fmt.Errorf("after %d attempts, last error: %s", attempts, err)

}


查看完整回答
反对 回复 2022-08-24
?
潇湘沐

TA贡献1816条经验 获得超6个赞

我知道这是一个古老的问题,但在搜索重试时遇到了它,并将其用作解决方案的基础。


此版本可以接受具有 2 个返回值的函数,并在 golang 1.18 中使用泛型来实现这一点。我在1.17中尝试过,但无法找到使该方法通用的方法。


这可以扩展到任何类型的任意数量的返回值。我在这里使用过,但可以限制为类型列表。any


func retry[T any](attempts int, sleep int, f func() (T, error)) (result T, err error) {

    for i := 0; i < attempts; i++ {

        if i > 0 {

            log.Println("retrying after error:", err)

            time.Sleep(time.Duration(sleep) * time.Second)

            sleep *= 2

        }

        result, err = f()

        if err == nil {

            return result, nil

        }

    }

    return result, fmt.Errorf("after %d attempts, last error: %s", attempts, err)

}

用法示例:


var config Configuration


something, err := retry(config.RetryAttempts, config.RetrySleep, func() (Something, error) { return GetSomething(config.Parameter) })


func GetSomething(parameter string) (something Something, err error) {

    // Do something flakey here that might need a retry...

    return something, error

}

希望这能帮助与我具有相同用例的人。


查看完整回答
反对 回复 2022-08-24
?
慕码人8056858

TA贡献1803条经验 获得超6个赞

您正在调用的函数正在使用上下文。因此,处理该上下文非常重要。


如果您不知道上下文是什么以及如何使用它,我会推荐该帖子:https://blog.golang.org/context


重试函数还应处理上下文。只是为了让你走上正轨,我给你一个简单的实现。


func retryMyServiceFunction(ctx context.Context, place uint, length uint, sleep time.Duration) {

    for {

        select {

        case ctx.Done():

            return

        default:

            err := MyServiceFunction(ctx, place, length)

            if err != nil {

                log.Println("handle error here!", err)

                time.Sleep(sleep)

            } else {

                return

            }

        }

    }

}

我不喜欢睡眠部分。因此,您应该分析返回的错误。此外,您还必须考虑超时。当您让服务长时间休眠时,可能会有超时。


查看完整回答
反对 回复 2022-08-24
?
白衣非少年

TA贡献1155条经验 获得超0个赞

在GoPlayground中接受的答案的评论中,我会考虑添加一些东西。在 for 循环中使用 continue 和 break 会使循环更加简单,因为不使用该语句。此外,我会在所有函数中使用早期返回来直接返回错误。最后,我会一直使用错误来检查函数是否失败,检查值的有效性应该在执行的函数本身内部。if i > 0 {


这将是我的小尝试:


package main


import (

    "errors"

    "fmt"

    "log"

    "time"

)


func main() {

    var complicatedFunctionPassing bool = false

    var attempts int = 5


    // if complicatedFunctionPassing is true retry just makes one try

    // if complicatedFunctionPassing is false retry makes ... attempts

    err := retry(attempts, time.Second, func() (err error) {

        if !complicatedFunctionPassing {

            return errors.New("somthing went wrong in the important function")

        }

        log.Println("Complicated function passed")

        return nil

    })

    if err != nil {

        log.Printf("failed after %d attempts with error: %s", attempts, err.Error())

    }

}


func retry(attempts int, sleep time.Duration, f func() error) (err error) {

    for i := 0; i < attempts; i++ {

        fmt.Println("This is attempt number", i+1)

        // calling the important function

        err = f()

        if err != nil {

            log.Printf("error occured after attempt number %d: %s", i+1, err.Error())

            log.Println("sleeping for: ", sleep.String())

            time.Sleep(sleep)

            sleep *= 2

            continue

        }

        break

    }

    return err

}

你可以在这里尝试一下:https://go.dev/play/p/Ag8ObCb980U


查看完整回答
反对 回复 2022-08-24
  • 4 回答
  • 0 关注
  • 124 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信