0

我正在尝试制作一个可以按以下方式工作的功能:

  1. 一旦调用服务函数,它就使用 Fetch 函数从服务中获取记录(以字节数组的形式出现),JSON 解组字节数组,填充结构,然后将结构发送到 DB 函数保存到数据库。
  2. 现在,由于这需要是一个连续的工作,我添加了两个 if 条件,如果接收到的记录长度为 0,那么我们使用 retry 函数重试提取记录,否则我们只是写入数据库。

我一直在尝试调试重试功能一段时间,但它只是不起作用,并且在第一次重试后基本上停止(即使我将尝试指定为 100)。我能做些什么来确保它不断重试提取记录?

代码如下:

// RETRY FUNCTION
func 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 = place

records, err := s.Poll(context.Background(), place, length)
if err != nil {
    logging.Info(err)
}

// if no records found then retry.
if len(records.Messages) == 0 {
    
    err = retry(100, 2*time.Minute, func() (err error) {
        records, err := s.Poll(context.Background(), place, length)
        
        // if data received, write to DB
        if len(records.Messages) != 0 {
            err = s.SaveData(records, lastSentPlace)
        }
        return
    })
    // if data is not received, or if err is not null, retry
    if err != nil || len(records.Messages) == 0 {
        log.Println(err)
        return
    }
// if data received on first try, then no need to retry, write to db 
} else if len(records.Messages) >0 {
    err = s.SaveData(records, lastSentPlace)
    if err != nil {
        return err
    }
}

return nil }

我认为,问题出在我尝试实现重试功能的方式上,我一直在尝试调试它一段时间,但是对于语言来说是新手,我真的被卡住了。我想做的是,如果没有找到记录,则实施退避。任何帮助是极大的赞赏。

谢谢 !!!

4

4 回答 4

5

我做了一个更简单的重试。

  • 使用更简单的 for 循环逻辑来确保正确性。
  • 我们在执行重试之前休眠,因此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)
}
于 2021-04-13T06:40:20.503 回答
0

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

如果您不知道上下文是什么以及如何使用它,我会推荐该帖子: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
            }
        }
    }
}

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

于 2021-04-13T07:18:40.163 回答
0

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

这将是我的小尝试:

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-01-21T12:19:28.057 回答
0

有一个用于重试机制的库。 https://github.com/avast/retry-go

url := "http://example.com"
var body []byte

err := retry.Do(
    func() error {
        resp, err := http.Get(url)
        if err != nil {
            return err
        }
        defer resp.Body.Close()
        body, err = ioutil.ReadAll(resp.Body)
        if err != nil {
            return err
        }

        return nil
    },
)

fmt.Println(body)
于 2021-04-13T06:33:42.027 回答