[译] 初学者需要了解的Go语言中的HTTP timeout

栏目: IT技术 · 发布时间: 4年前

内容简介:原文链接​ 对于提高分布式系统的可用性,请求超时是非常重要的一个部分,当系统某个部分出现故障的时超时机制可以降低故障对整个分布式系统的影响,就如下面这条twitter中提到的。在go语言中应该如何合理的模拟一个504 http.StatusGatewayTimeout响应呢?

原文链接 itnext.io/http-reques…

​ 对于提高分布式系统的可用性,请求超时是非常重要的一个部分,当系统某个部分出现故障的时超时机制可以降低故障对整个分布式系统的影响,就如下面这条twitter中提到的。

[译] 初学者需要了解的 <a href='https://www.codercto.com/topics/6127.html'>Go</a> 语言中的HTTP timeout

问题

在go语言中应该如何合理的模拟一个504 http.StatusGatewayTimeout响应呢?

之前在开发一个OAuth token授权功能的时候,我曾试着用 httptest 去模拟服务端超时并返回 504 http.StatusGatewayTimeout 响应,然而我实现的效果却是客户端由于没有在设定的时间内得到响应而超时退出,而不是服务端返回了504的status code。如同大多数,当时我像下面这样使用标准库的HTTP包去创建一个client对象并指定timeout属性:

client := http.Client{Timeout: 5 * time.Second}
复制代码

需要发起http请求时,创建上面这样一个http client对象看起来是一个非常简单和直接的方式。然而很多关于请求超时的细节被忽视了,包括客户端超时、服务端超时和负载均衡器的超时。

客户端超时

在客户端,http请求超时有多种不同的定义方式,取决于你关注整个请求-响应周期的那个部分。具体说来,一个完整的请求-响应周期由 Dialer (三次握手), TLS握手 , 请求头及请求体的生成和发送,响应头及响应体的接收。除了定义一个完整的请求-响应周期的超时时间之外,go语言还支持定义这个周期的某个组成部分的超时时间,有如下三个常用的方式:

http.Client
context
http.Transport

http.Client

通过 http.Client 可以定义从三次握手(Dialer)到接收到响应体的一个完整的请求-响应周期的超时时间。 http.Client 结构有一个可选的类型为 time.DurationTimeout 字段

client := http.Client{Timeout: 5 * time.Second}
复制代码

Context

go语言的 context 包提供了 WithTimeout , WithDeadline , WithCancel 三个实用的方法分别去实现具有超时时间的,具有过期时间的和可以手动取消的http请求。使用context包的 WithTimeout 方法,配合上 http.Request 对象的 WithContext 方法,我们可以控制从请求发送到到手响应之间超时时间(不包括TCP三次握手和TLS握手的耗时):

ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
req, err := http.NewRequest("GET", url, nil)
if err != nil {
    t.Error("Request error", err)
}

resp, err := http.DefaultClient.Do(req.WithContext(ctx))
复制代码

http.Transport

通过使用自定义的 http.Transport 并指定 DialContext 属性来创建http.Client对象,可以控制Dialer的超时时间(即三次握手的超时时间):

transport := &http.Transport{
    DialContext: (&net.Dialer{   
        Timeout: timeout,
    }).DialContext,
}
client := http.Client{Transport: transport}
复制代码

解决方案

基于上面的问题分析和可选方案,我尝试通过 context.WithTimeout 来控制 http.Request 的超时时间。然而得到了如下的error:

client_test.go:40: Response error Get http://127.0.0.1:49597: context deadline exceeded
复制代码

这并没有解决我的问题,因为我想实现服务端返回 504 http.StatusGatewayTimout 的响应。

服务端超时

上述在客户端使用 context.WithTimeout() 的方案,当设定的时间内没有完成 请求-响应 时,客户端发起http请求的方法终止并且返回了一个error,而不是我想要的服务端返回了504的http status code。

通过下面的方式可以让httptest server每次都返回超时的状态码:

httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request){
    w.WriteHeader(http.StatusGatewayTimeout)
}))
复制代码

然而如果想让服务端在处理客户端请求超时时返回504 status code,我们可以在服务端程序里用 http.TimeoutHandler 去装饰一下原本的handler来实现:

func TestClientTimeout(t *testing.T) {
    handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        d := map[string]interface{}{
            "id":    "12",
            "scope": "test-scope",
        }

        time.Sleep(100 * time.Millisecond) //<- Any value > 20ms
        b, err:= json.Marshal(d)
        if err != nil {
            t.Error(err)
        }
        io.WriteString(w, string(b))
        w.WriteHeader(http.StatusOK)
    })

    backend := httptest.NewServer(http.TimeoutHandler(handlerFunc, 20*time.Millisecond, "server timeout"))

    url := backend.URL
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        t.Error("Request error", err)
        return
    }

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        t.Error("Response error", err)
        return
    }

    defer resp.Body.Close()
}
复制代码

对于刚接触go语言的gopher来说,理解这些上层的http timeout的工作原理非常有用!如果你想了解更多go语言中关于http timeout的细节,一直要读一下这篇来自 Cloudflare 的文章。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

精益创业实战

精益创业实战

Ash Maurya / 张玳 / 图灵文化发展有限公司 / 2013-1 / 39.00元

《精益创业实战(第2版)》融合了精益创业法、客户开发、商业模式画布和敏捷/持续集成的精华,讲解精益创业实战法。作者以自己的创业项目为主线,结合大量真实案例,并融入一些伟大创业者的智慧,创建了一套思考、验证和发布产品的系统。那些想要验证自己的创意、解决实际问题和渴望拥有成功事业的人,可以把《精益创业实战(第2版)》当成一套明确的实践计划、一幅清晰的创业路线图、一本实践指南,或者一套反复实践的方法论。一起来看看 《精益创业实战》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试