Home  >  Article  >  Backend Development  >  How to create context using timeouts and retries in Go?

How to create context using timeouts and retries in Go?

PHPz
PHPzforward
2024-02-08 23:20:19710browse

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

php editor Xinyi will introduce how to use timeout and retry to create context in Go language. When writing code, we often encounter situations where we need to limit the execution time of an operation or retry when the operation fails. Go language provides a simple and powerful mechanism, context, which can help us achieve these needs. By properly utilizing timeouts and retry mechanisms, we can improve the reliability and stability of the code while improving the user experience. In the next article, we will discuss in detail how to use timeouts and retries to create context in Go language.

Question content

I try to create context in go using timeout and multiple retries. Here is the code sample

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
            }
        }
    }
}

Then, I call readretry result := readretry(port, 2, 5) // 2 second timeout, 5 retries. But if the data is not ready for the first time, then reader cannot write to rcvch. Maybe it’s full? Why? If I try to close the channel at the end of readwithcontext execution, a conflict occurs - writing to a closed channel. Where is the collision? It believes that readwithcontext will be started as a new instance every time, creating a new instance of rcvch. If reader is closed due to timeout, all chain functions and Its local variables (including channels) have been destroyed. However, it seems I made a mistake. So, how to retry? See what the log looks like:

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

Workaround

Each retry will create a new reader and a new channel. If readWithContext times out, the reader is still there waiting, and may eventually read, but now no one is listening on the other end of the channel, so the reader is leaked.

Have a reader goroutine and a channel, use readWithContext to read from it. You must also stop the reader if the context expires and all retries are exhausted.

The above is the detailed content of How to create context using timeouts and retries in Go?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete