卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章34488本站已运行393

如何在 Go 中使用超时和重试来创建上下文?

如何在 go 中使用超时和重试来创建上下文?

php小编新一将为大家介绍如何在Go语言中使用超时和重试来创建上下文。在编写代码时,我们经常会遇到需要限制某个操作的执行时间或者在操作失败时进行重试的情况。Go语言提供了一种简洁而强大的机制,即上下文(context),它可以帮助我们实现这些需求。通过合理地利用超时和重试机制,我们可以提高代码的可靠性和稳定性,同时提升用户体验。在接下来的文章中,我们将详细讨论如何在Go语言中使用超时和重试来创建上下文。

问题内容

我尝试在 go 中使用超时和多次重试来创建上下文。 这是代码示例

func readretry(port io.readwritecloser, timeout, cnt int) []byte {
    fmt.println("in read retry")
    for i := 0; i < cnt; i++ {
        fmt.println("read attempt:", i)
        res := readwithcontext(timeout, port)
        if res != nil {
            return res
        }
    }
    return nil
}

func readwithcontext(timeout int, port io.readwritecloser) []byte {
    fmt.println("in readwithcontext")
    fmt.println("opening channel")
    rcvch := make(chan []byte)
    ctx, cancel := context.withtimeout(context.background(), time.duration(time.second*time.duration(timeout)))
    defer cancel()

    go reader(ctx, port, rcvch)

    for {
        select {
        case <-ctx.done():
            fmt.println("reader: context cancelled")
            return nil
        case buf := <-rcvch:
            fmt.println("reader: got data")
            return buf
        }
    }
}

func reader(ctx context.context, port io.readwritecloser, rcvch chan []byte) {

    fmt.println("in reader")

    answ := make([]byte, 1024)
    buf := bytes.buffer{}
    var err error

    for {
        i := 0
        i, err = port.read(answ)
        if err != nil && err != io.eof {
            log.printf("port.read: %v", err)
        }
        if i != 0 {
            answ = answ[:i]
            buf.write(answ)
            if buf.bytes()[len(buf.bytes())-1] == delimiter {
                fmt.print("received: ")
                printbuf(buf.bytes())
                rcvch <- buf.bytes() //if there is no data in the first attempt, cannot write to the channel here!!
                return
            }
        }
    }
}

然后,我调用 readretry result := readretry(port, 2, 5) // 2 秒超时,5 次重试。但如果第一次数据没有准备好,那么 reader 无法写入 rcvch 。可能已经满了?为什么?如果我尝试在 readwithcontext 执行结束时关闭通道,则会发生冲突 - 写入已关闭的通道。碰撞在哪里?它认为,readwithcontext每次都会作为一个新实例启动,创建一个rcvch的新实例,如果reader因超时而关闭,则所有链函数及其局部变量(包括通道)已被破坏。但是,看来我犯了一个错误。那么,如何进行重试呢? 看看日志是什么样子的:

IN READ RETRY
Read attempt: 1
IN readWithContext
Opening channel
IN reader
Start reader
Received: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0d //<- data is received, but not written to the channel!!
reader: context cancelled

解决方法

每次重试都会创建一个新的阅读器和一个新的频道。如果 readWithContext 超时,读者仍然在那里等待,并且可能最终阅读,但现在通道的另一端没有人在监听,因此读者被泄露。

有一个 reader goroutine 和一个通道,使用 readWithContext 从中读取。如果上下文过期并且所有重试都用尽,您也必须停止读取器。

卓越飞翔博客
上一篇: 是否可以在运行时更新 zap 记录器的日志级别?
下一篇: 返回列表
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏