go的net/http有哪些值得关注的细节? 4

简介: go的net/http有哪些值得关注的细节?

别设置 Transport.Dail里的SetDeadline

http.Transport.Dial的配置里有个SetDeadline,它表示连接建立后发送接收数据的超时时间。听起来跟client.Timeout很像。

那么他们有什么区别呢?我们通过一个例子去看下。

package main
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net"
    "net/http"
    "time"
)
var tr *http.Transport
func init() {
    tr = &http.Transport{
        MaxIdleConns: 100,
        Dial: func(netw, addr string) (net.Conn, error) {
            conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时
            if err != nil {
                return nil, err
            }
            err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时
            if err != nil {
                return nil, err
            }
            return conn, nil
        },
    }
}
func main() {
    for {
        _, err := Get("http://www.baidu.com/")
        if err != nil {
            fmt.Println(err)
            break
        }
    }
}
func Get(url string) ([]byte, error) {
    m := make(map[string]interface{})
    data, err := json.Marshal(m)
    if err != nil {
        return nil, err
    }
    body := bytes.NewReader(data)
    req, _ := http.NewRequest("Get", url, body)
    req.Header.Add("content-type", "application/json")
    client := &http.Client{
        Transport: tr,
    }
    res, err := client.Do(req)
    if res != nil {
        defer res.Body.Close()
    }
    if err != nil {
        return nil, err
    }
    resBody, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return nil, err
    }
    return resBody, nil
}


上面这段代码,我们设置了SetDeadline为3s,当你执行一段时间,会发现请求baidu会超时,但其实baidu的接口很快,不可能超过3s。

在生产环境中,假如是你的服务调用下游服务,你看到的现象就是,你的服务显示3s超时了,但下游服务可能只花了200ms就已经响应你的请求了,并且这是随机发生的问题。遇到这种情况,我们一般会认为是“网络波动”。

但如果我们去对网络抓包,就很容易发现问题的原因 。

抓包结果

可以看到,在tcp三次握手之后,就会开始多次网络请求。直到3s的时候,就会触发RST包,断开连接。也就是说,我们设置的SetDeadline,并不是指单次http请求的超时是3s,而是指整个tcp连接的存活时间是3s,计算长连接被连接池回收,这个时间也不会重置。

SetDeadline的解释

我实在想不到什么样的场景会需要这个功能,因此我的建议是,不要使用它。

下面是修改后的代码。这个问题其实在我另外一篇文章有过详细的解释,如果你对源码解析感兴趣的话,可以去看看。

package main
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)
var tr *http.Transport
func init() {
    tr = &http.Transport{
        MaxIdleConns: 100,
        // 下面的代码被干掉了
        //Dial: func(netw, addr string) (net.Conn, error) {
        // conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时
        // if err != nil {
        //  return nil, err
        // }
        // err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时
        // if err != nil {
        //  return nil, err
        // }
        // return conn, nil
        //},
    }
}
func Get(url string) ([]byte, error) {
    m := make(map[string]interface{})
    data, err := json.Marshal(m)
    if err != nil {
        return nil, err
    }
    body := bytes.NewReader(data)
    req, _ := http.NewRequest("Get", url, body)
    req.Header.Add("content-type", "application/json")
    client := &http.Client{
        Transport: tr,
        Timeout: 3*time.Second,  // 超时加在这里,是每次调用的超时
    }
    res, err := client.Do(req) 
    if res != nil {
        defer res.Body.Close()
    }
    if err != nil {
        return nil, err
    }
    resBody, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return nil, err
    }
    return resBody, nil
}
func main() {
    for {
        _, err := Get("http://www.baidu.com/")
        if err != nil {
            fmt.Println(err)
            break
        }
    }
}

总结

golang的net/http部分有不少细节点,直接上源码分析怕劝退不少人,所以希望以几个例子作为引子展开话题然后深入了解它的内部实现。总体内容比较碎片化,但这个库的重点知识点基本都在这里面了。希望对大家后续排查问题有帮助。

最后

离开广东好长时间了,好久没人叫我靓仔了。

大家可以在评论区里,叫我一靓仔吗?

我这么善良质朴的愿望,能被满足吗?

如果实在叫不出口的话,能帮我点下右下角的点赞和在看吗?


别说了,一起在知识的海洋里呛水吧

目录
相关文章
|
8月前
|
JSON 中间件 Go
Go 网络编程:HTTP服务与客户端开发
Go 语言的 `net/http` 包功能强大,可快速构建高并发 HTTP 服务。本文从创建简单 HTTP 服务入手,逐步讲解请求与响应对象、URL 参数处理、自定义路由、JSON 接口、静态文件服务、中间件编写及 HTTPS 配置等内容。通过示例代码展示如何使用 `http.HandleFunc`、`http.ServeMux`、`http.Client` 等工具实现常见功能,帮助开发者掌握构建高效 Web 应用的核心技能。
446 61
|
6月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
7月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
8月前
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
7月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
537 0
|
9月前
|
Go
在golang中发起http请求以获取访问域名的ip地址实例(使用net, httptrace库)
这只是追踪我们的行程的简单方法,不过希望你跟着探险家的脚步,即使是在互联网的隧道中,也可以找到你想去的地方。接下来就是你的探险之旅了,祝你好运!
518 26
|
10月前
|
中间件 Go
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
526 35
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
最近在线上往hbase导数据,因为hbase写入能力比较强,没有太在意写的问题。让业务方进行历史数据的导入操作,中间发现一个问题,写入速度太快,并且业务数据集中到其中一个region,这个region无法split掉,处于不可用状态。
1517 0
|
Web App开发 监控 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
Hbase依赖的datanode日志中如果出现如下报错信息:DataXceiverjava.io.EOFException: INFO org.apache.hadoop.hdfs.server.datanode.DataNode: Exception in receiveBlock for block  解决办法:Hbase侧配置的dfs.socket.timeout值过小,与DataNode侧配置的 dfs.socket.timeout的配置不一致,将hbase和datanode的该配置调成大并一致。
941 0