dropbox1111 2017-04-10 11:23
浏览 133
已采纳

使用golang在每个请求上执行上下文超时

I am trying to handle context timeout for every request. We have following server structures:

enter image description here

Flow overview:

Go Server: Basically, acts as a [Reverse-proxy].2

Auth Server: Check for requests Authentication.

Application Server: Core request processing logic.

Now if Authorization server can't able to process a request in stipulated time, then I want to close the goroutine from memory.

Here is what I tried:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequest("GET", authorizationServer, nil)
req.Header = r.Header
req.WithContext(ctx)
res, error := client.Do(req)
select {
case <-time.After(10 * time.Second):
    fmt.Println("overslept")
case <-ctx.Done():
    fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}

Over here, context returns as "deadline exceeded", if request is not processed in stipulated time. But It continues to process that request and return response in more that specified time. So How can I stop request flow(goroutine), when time exceeded.

Although I've also implemented complete request needs to be processed in 60 seconds with this code:

var netTransport = &http.Transport{
    Dial: (&net.Dialer{
        Timeout: 60 * time.Second,
    }).Dial,
    TLSHandshakeTimeout: 60 * time.Second,
}
client := &http.Client{
    Timeout:   time.Second * 60,
    Transport: netTransport,
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return http.ErrUseLastResponse
    },
}

So do I need any seperate context implementations as well? Thanks in advance for help.

Note1: It will be awesome, If we can manage timeout on every requests(goroutine) created by HTTP server, using context.

  • 写回答

1条回答 默认 最新

  • dpps0715 2017-04-10 11:47
    关注

    What happens in your code is very correct and behaves as expected.

    You create a context with 5 seconds timeout. You pass it to the request and make that request. Let's say that request returns in 2 seconds. You then do a select and either wait 10 seconds or wait for the context to finish. Context will always finish in the initial 5 seconds from when it was created and will also give that error every time it reaches the end.

    The context is independent of the request and it will reach it's deadline unless, cancelled previously. You cancel the request when the function finishes using defer.

    In your code the request takes your timeout in consideration. But the ctx.Err() will return deadline exceeded everytime it reaches the timeout. Since that's what happens inside the context. calling ctx.Err() multiple times will return the same error.

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    go func () {
        select {
        case <-time.After(10 * time.Second):
            fmt.Println("overslept")
        case <-ctx.Done():
            fmt.Println(ctx.Err()) // prints "context deadline exceeded"
        }
    }()
    req, _ := http.NewRequest("GET", authorizationServer, nil)
    req.Header = r.Header
    req = req.WithContext(ctx)
    res, error := client.Do(req)
    

    From the context documentation:

    // Err returns a non-nil error value after Done is closed. Err returns
    // Canceled if the context was canceled or DeadlineExceeded if the
    // context's deadline passed. No other values for Err are defined.
    // After Done is closed, successive calls to Err return the same value.
    

    In your code, the timeout will always be reached and not cancelled, that is why you receive DeadlineExceeeded. Your code is correct except the select part which will block until either 10 seconds pass or context timeout is reached. In your case always the context timeout is reached.

    You should check the error returned by the client.Do call and not worry about the context error in here. You are the one controlling the context. If the request timeouts, a case you should test of course, then a proper error would be returned for you to verify.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 前端echarts坐标轴问题
  • ¥15 CMFCPropertyPage
  • ¥15 ad5933的I2C
  • ¥15 请问RTX4060的笔记本电脑可以训练yolov5模型吗?
  • ¥15 数学建模求思路及代码
  • ¥50 silvaco GaN HEMT有栅极场板的击穿电压仿真问题
  • ¥15 谁会P4语言啊,我想请教一下
  • ¥15 这个怎么改成直流激励源给加热电阻提供5a电流呀
  • ¥50 求解vmware的网络模式问题 别拿AI回答
  • ¥24 EFS加密后,在同一台电脑解密出错,证书界面找不到对应指纹的证书,未备份证书,求在原电脑解密的方法,可行即采纳