使用golang在每个请求上执行上下文超时 [英] Context timeout implementation on every request using golang

查看:216
本文介绍了使用golang在每个请求上执行上下文超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试处理每个请求的上下文超时。我们有以下服务器结构:





流程概览:

Go Server :基本上,可充当[Reverse-proxy]。 2


$ b

身份验证服务器:检查身份验证请求。


$ b

Application Server :核心请求处理逻辑。现在如果授权服务器不能够在规定的时间内处理请求,然后我想关闭内存中的goroutine。



以下是我试过的:

  ctx,cancel:= context.WithTimeout(context.Background(),5 * time.Second)
推迟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())//输出超出上下文截止日期
}

如果请求未在规定的时间内处理,则在此处上下文会以超过期限的形式返回。但它会继续处理该请求并在特定时间内返回响应。那么如何在超过时间的情况下停止请求流(goroutine)。



尽管我已经实现了完整的请求,需要在60秒内使用此代码进行处理:

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

那么我是否还需要任何单独的上下文实现?如果我们可以管理由HTTP服务器创建的每个请求(goroutine)的超时时间,那么将会非常棒。

,使用上下文。

解决方案

您的代码中发生的情况非常正确,并且表现得如预期。

您创建一个上下文,其中 5秒超时。您将它传递给请求并发出请求。假设该请求在2秒内返回。然后执行选择并等待10秒钟或等待上下文完成。上下文将始终在创建时的最初5秒内完成,并且每次达到结束时都会出现该错误。



上下文与请求无关,并且会在截止日期前完成,除非之前取消。当函数使用 defer 时取消请求。



在您的代码中,请求会考虑您的超时。但是,每当达到超时时, ctx.Err()都会返回超过期限。因为这是上下文内发生的情况。

  ctx,cancel:= context.WithTimeout(context.Background (),5 * time.Second)
推迟取消()

去func(){
select {
case< -time.After(10 * time.Second):
fmt.Println(overslept)
case< -ctx.Done():
fmt.Println(ctx.Err())//打印上下文截止日期超过

}()
req,_:= http.NewRequest(GET,authorizationServer,nil)
req.Header = r.Header
req = req.WithContext(ctx)
res,error:= client.Do(req)

从上下文文档中:

  //在完成关闭后,Err返回一个非零错误值。 Err返回
//如果上下文被取消,则取消;如果
上下文的最后期限已过,则取消DeadlineExceeded。没有定义Err的其他值。
//完成关闭后,对Err的连续调用返回相同的值。

在您的代码中,超时总是会到达并且不会被取消,这就是为什么您会收到 DeadlineExceeeded 。你的代码是正确的,除了选择的部分将阻塞,直到10秒通过或上下文超时达到。在你的情况下,总是达到上下文超时。



你应该检查<$ c $返回的错误 c> client.Do 调用,不用担心这里的上下文错误。你是控制上下文的人。如果请求超时,那么当然应该测试一个案例,那么将会返回一个适当的错误以供您验证。

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

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.

解决方案

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.

这篇关于使用golang在每个请求上执行上下文超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆