使用两个context实现CLOSE包的超时等待

在UDP中,一般发送者发送包后,如果一定的时间对方没有收到,就需要重传。例如UDP实现握手的过程,如果握手的包,比如RTMFP协议的IHELLO,发送给对方后,如果一定1秒没有收到,就应该重发一次,然后等3秒、6秒、9秒,如果最后没有收到就是超时了。

最后一个Close包,发送者不能等待这么长的时间,所以需要设置一个较短的时间做超时退出。一般收发都是一个context,在最后这个Close包时,收到ctx.Done也不能立刻退出,因为还需要稍微等待,譬如600毫秒如果没有收到响应才能退出。

一个可能的实现是这样:

in := make(chan []byte)

func Close(ctx context.Context) (err error) {
    timeous := ... // 1s,3s,6s,9s...
    for _, to := range timeouts {
        // 发送给对方WriteToUDP("CLOSE", peer)
        // 另外一个goroutine读取UDP包到in

        select {
        case <- time.After(to):
        case <- in:
            fmt.Println("Close ok")
            return
        case <- ctx.Done():
            fmt.Println("Program quit")
            return
        }
    }
    return
}

但是这个问题在于,在程序退出时,一般都会cancel ctx然后调用Close方法,这个地方就不会等待任何的超时,就打印"Program quit"然后返回了。解决方案是用另外一个context。但是如何处理之前的ctx的done呢?可以再起一个goroutine做同步:

in := make(chan []byte)

func Close(ctx context.Context) (err error) {
    ctxRead,cancelRead := context.WithCancel(context.Background())
    go func(){ // sync ctx with ctxRead
        select {
            case <-ctxRead.Done():
            case <-ctx.Done():
                select {
                    case <-ctxRead.Done():
                    case <-time.After(600*time.Milliseconds):
                        cancelRead()
                }
        }
    }()

    ctx = ctxRead // 下面直接用ctxRead。
    timeous := ... // 1s,3s,6s,9s...
    for _, to := range timeouts {
        // 发送给对方WriteToUDP("CLOSE", peer)
        // 另外一个goroutine读取UDP包到in

        select {
        case <- time.After(to):
        case <- in:
            fmt.Println("Close ok")
            return
        case <- ctx.Done():
            fmt.Println("Program quit")
            return
        }
    }
    return
}

这样在主要的逻辑中,还是只需要处理ctx,但是这个ctx已经是新的context了。不过在实际的过程中,这个sync的goroutine需要确定起来后,才能继续,否则会造成执行顺序不确定:

sc := make(chan bool, 1)
go func(){ // sync ctx with ctxRead
    sc <- true
    select {
    ......
}
<- sc

使用context,来控制多个goroutine的执行和取消,是非常好用的,关键可以完全关注业务的逻辑,而不会引入因为ctx取消或者超时机制而造成的特殊逻辑。

0 个评论

要回复文章请先登录注册