• 欢迎来到本博客,希望可以y一起学习与分享

go 网络编程 之 HTTP 及 原理

笔记 benz 3周前 (09-28) 7次浏览 0个评论 扫描二维码
文章目录[隐藏]

使用

HTTP使用的包是GO提供的包:net/http

简单实用

Get请求

Post请求

Tips:使用这个方法的话,第二个参数要设置成application/x-www-form-urlencoded,否则post参数无法传递。
表单提交

复杂的请求
有时需要在请求的时候设置头参数、cookie之类的数据,就可以使用http.Do方法。

原理

http请求流程

  1. 创建http.Client对象client
  2. 创建http.Request对象req
  3. 发送请求client.do(req)
  4. 关闭resp.Body.Close()

即使直接调用client.Get()client.Post(), 内部同样创建了request, 且最终总是通过client.Do()方法调用私有的client.do()方法, 执行请求;


http请求核心类

  • http.Client
  • http.Request
  • http.Transport

http.Client

该类主要功能:

  • Cookie
  • Timeout
  • Redirect
  • Transport

我们还可以基于 http.Client 自定义 HTTP 客户端实现,在此之前,我们先来看看 Client 类型的数据结构:

其中 Transport 字段必须实现 http.RoundTripper 接口,Transport 指定了一次 HTTP 事务(请求响应)的完整流程,如果不指定 Transport,默认会使用 http.DefaultTransport 这个默认实现,比如 http.DefaultClient 就是这么做的,后面我们会深入探讨 http.DefaultTransport 的底层实现。

CheckRedirect 函数用于定义处理重定向的策略。当使用 HTTP 默认客户端提供的 Get() 或者 Head() 方法发送 HTTP 请求时,如果响应状态码为 30x (比如 301302 等),HTTP 客户端会在遵循跳转规则之前先调用这个 CheckRedirect 函数。

Jar 可用于在 HTTP 客户端中设置 Cookie,Jar 类型必须实现 http.CookieJar 接口,该接口预定义了 SetCookies()Cookies() 两个方法。如果 HTTP 客户端中没有设置 Jar,Cookie 将被忽略而不会发送到客户端。实际上,我们一般都用 http.SetCookie() 方法来设置 Cookie。

Timeout 字段用于指定 Transport 的超时时间,没有指定的话则使用 Transport 自定义的设置。

http.Transport  (重点

  • Transport用来缓存连接, 以供将来重用, 而不是根据需要创建
  • Transport是并发安全的
  • Transport仅是用来发送HTTP或HTTPS的低级功能, 像cookie和redirect等高级功能是http.Client实现的

transport实现了RoundTripper接口,该接口只有一个方法RoundTrip(),故transport的入口函数就是RoundTrip()。transport的主要功能其实就是缓存了长连接,用于大量http请求场景下的连接复用,减少发送请求时TCP(TLS)连接建立的时间损耗,同时transport还能对连接做一些限制,如连接超时时间,每个host的最大连接数等。transport对长连接的缓存和控制仅限于TCP+(TLS)+HTTP1,不对HTTP2做缓存和限制。

tranport包含如下几个主要概念:

  • 连接池:在idleConn中保存了不同类型(connectMethodKey)的请求连接(persistConn)。当发生请求时,首先会尝试从连接池中取一条符合其请求类型的连接使用
  • readLoop/writeLoop:连接之上的功能,循环处理该类型的请求(发送request,返回response)
  • roundTrip:请求的真正入口,接收到一个请求后会交给writeLoop和readLoop处理。

一对readLoop/writeLoop只能处理一条连接,如果这条连接上没有更多的请求,则关闭连接,退出循环,释放系统资源。

Transport结构体中的主要成员如下(没有列出所有成员):

结合 Transport 数据结构来看下 DefaultTransport 的设置:

  • 通过 net.Dialer 初始化 Dial 上下文配置,默认超时时间设置为 30 秒;
  • 通过 MaxIdleConns 指定最大空闲连接数为 100,未显式设置 MaxIdleConnsPerHostMaxConnsPerHostMaxIdleConnsPerHost 有默认值,通过 http.DefaultMaxIdleConnsPerHost 设置,对应缺省值是 2;
  • 通过 IdleConnTimeout 指定最大空闲连接时间为 90 秒,即当某个空闲连接超过 90 秒没有被复用,则销毁,空闲连接需要 DisableKeepAlivesfalse 的情况下才可用,即 HTTP 长连接状态下有效(HTTP/1.1以上版本支持长连接,对应请求头 Connection:keep-alive);
  • 通过 TLSHandshakeTimeout 指定基于 TLS 协议的安全 TCP 连接在被建立时握手阶段的超时时间为 10 秒;
  • 通过 ExpectContinueTimeout 指定客户端想要使用 POST 请求把一个很大的报文体发送给服务端的时候,先通过发送一个包含了 Expect: 100-continue 的请求报文头,来询问服务端是否愿意接收这个大报文体对应的超时时间,这里默认设置为 1 秒。

另外,Transport 包含了 RoundTrip 方法实现,所以实现了 RoundTripper 接口。下面我们来看看 TransportRoundTrip 方法的实现。

 

Transport.roundTrip 是主入口,它通过传入一个request参数,由此选择一个合适的长连接来发送该request并返回response。整个流程主要分为两步:

使用getConn函数来获得底层TCP(TLS)连接;调用roundTrip函数进行上层协议(HTTP)处理。


getConn用于返回一条长连接。长连接的来源有2种路径:连接池中获取;当连接池中无法获取到时会新建一条连接

tryPutIdleConn函数用来将一条新创建或回收的连接放回连接池中,以便后续使用。与getIdleConnCh配合使用,后者用于获取一类连接对应的chan。在如下场景会将一个连接放回idleConn

  • readLoop成功之后(当然还有其他判断,如底层链路没有返回EOF错误);
  • 创建一个新连接且新连接没有被使用时;
  • roundTrip一开始发现request被取消时

dialConn用于新创建一条连接,并为该连接启动readLoopwriteLoop


addTLS用于进行非注册协议下的TLS协商

在获取到底层TCP(TLS)连接后在roundTrip中处理上层协议:即发送HTTP request,返回HTTP response。roundTripwriteLoop提供request,从readLoop获取response。

一个roundTrip用于处理一类request。

writeLoop用于发送request请求


readLoop循环接收response响应,成功获得response后会将连接返回连接池,便于后续复用。当readLoop正常处理完一个response之后,会将连接重新放入到连接池中;

readloop退出后,该连接会被关闭移除。

Client.do

该方法主要实现了:

  1. 参数检查
  2. 默认值设置
  3. 多跳请求
  4. 计算超时时间点deadline
  5. 调用c.send(req, deadline)

Client.send

该方法主要实现了:

  1. Cookie的装载
  2. Transport对象的获取
  3. 调用send(req, c.transport(), deadline)


Transport的默认值

http.send

该方法主要实现了:

  1. 参数校验: URL, header, RoundTripper
  2. 超时取消: setRequestCancel(req, rt, deadline)
  3. 请求事务: rt.RoundTrip(req)

client.setRequestCancel

该方法主要实现了:

创建一个协程利用select chan机制阻塞等待取消请求

Transport.RoundTrip

该方法主要实现了

  1. 参数校验: scheme, host, method, protocol…
  2. 获取缓存的或新建的连接

Transport.getConn

首先从连接池中获取连接t.getIdleConn(cm), 获取成功即返回

拨号创建新连接

  1. 如果达到了最大数量则阻塞, 等待空闲

参考

go http请求流程分析
详解golang net之transport
Go 语言网络编程系列(四)—— HTTP 编程篇:http.Client 底层实现剖析


文章 go 网络编程 之 HTTP 及 原理 转载需要注明出处
喜欢 (0)

您必须 登录 才能发表评论!