有关Http持久连接的一切,卷给你看

网络 通信技术
HTTP Keep-Alive 是在应用层对TCP连接进行滑动续约复用, 如果客户端/服务器稳定续约,就成了名副其实的长连接。

[[438351]]

上文中我的结论是: HTTP Keep-Alive 是在应用层对TCP连接进行滑动续约复用, 如果客户端/服务器稳定续约,就成了名副其实的长连接。

目前所有的Http网络库都默认开启了HTTP Keep-Alive,今天我们从底层TCP连接和排障角度撕碎HTTP持久连接。

“我只是一个写web程序的猿,我为什么要知道这么多??????”。

使用go语言倒腾一个httpServer/httpClient,粗略聊一聊go的使用风格。

使用go语言net/http包快速搭建httpserver,注入用于记录请求日志的Handler

  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  "log" 
  6.  "net/http" 
  7.  
  8. // IndexHandler记录请求的基本信息: 请关注r.RemoteAddr 
  9. func Index(w http.ResponseWriter, r *http.Request) { 
  10.  fmt.Println("receive a request from:", r.RemoteAddr, r.Header) 
  11.  w.Write([]byte("ok")) 
  12.  
  13. // net/http 默认开启持久连接 
  14. func main() {  
  15.  fmt.Printf("Starting server at port 8081\n"
  16.  if err := http.ListenAndServe(":8081", http.HandlerFunc(Index)); err != nil { 
  17.   log.Fatal(err) 
  18.  } 

ListenAndServe创建了默认的httpServer服务器,go通过首字母大小写来控制访问权限,如果首字母大写,则可以被外部包访问, 类比C#全局函数、静态函数。

  1. func ListenAndServe(addr string, handler Handler) error { 
  2.  server := &Server{Addr: addr, Handler: handler} 
  3.  return server.ListenAndServe() 

net/http服务器默认开启了Keep-Alive, 由Server的私有变量disableKeepAlives体现。

  1. type  Server  struct { 
  2.   ... 
  3.   disableKeepAlives int32     // accessed atomically.  
  4.   ... 

使用者也可以手动关闭Keep-Alive, SetKeepAlivesEnabled()会修改私有变量disableKeepAlives的值

  1. s := &http.Server{ 
  2.   Addr:           ":8081"
  3.   Handler: http.HandlerFunc(Index), 
  4.   ReadTimeout:    10 * time.Second
  5.   WriteTimeout:   10 * time.Second
  6.   MaxHeaderBytes: 1 << 20, 
  7.  } 
  8.  s.SetKeepAlivesEnabled(true
  9.  if err := s.ListenAndServe(); err != nil { 
  10.   log.Fatal(err) 
  11.  } 

以上也是go语言包的基本制作/使用风格。

请注意我在httpserver插入了IndexHander,记录httpclient的基本信息。

这里有个知识点:如果httpclient建立新的TCP连接,系统会按照一定规则给你分配随机端口。

启动服务器程序,浏览器访问localhost:8081,

服务器会收到如下日志, 图中红圈处表明浏览器使用了系统随机的固定端口建立tcp连接。

使用net/http编写客户端:间隔1s向服务器发起HTTP请求

  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  "io/ioutil" 
  6.  "log" 
  7.  "net/http" 
  8.  "time" 
  9.  
  10. func main() { 
  11.  client := &http.Client{ 
  12.   Timeout: 10 * time.Second
  13.  } 
  14.  for { 
  15.   requestWithClose(client) 
  16.   time.Sleep(time.Second * 1) 
  17.  } 
  18.  
  19. func requestWithClose(client *http.Client) { 
  20.  
  21.  resp, err := client.Get("http://127.0.0.1:8081"
  22.  
  23.  if err != nil { 
  24.   fmt.Printf("error occurred while fetching page, error: %s", err.Error()) 
  25.   return 
  26.  } 
  27.  defer resp.Body.Close() 
  28.  
  29.  c, err := ioutil.ReadAll(resp.Body) 
  30.  if err != nil { 
  31.   log.Fatalf("Couldn't parse response body. %+v", err) 
  32.  } 
  33.  
  34.  fmt.Println(string(c)) 

服务器收到的请求日志如下:

图中红框显示httpclient使用固定端口61799发起了http请求,客户端/服务器维持了HTTP Keep-alive。

使用netstat -an | grep 127.0.0.1:8081可围观系统针对特定ip的TCP连接:客户端系统中针对 服务端也只建立了一个tcp连接,tcp连接的端口是61799,与上文呼应。

使用Wireshark查看localhost网卡发生的tcp连接

可以看到每次http请求/响应之前均没有tcp三次握手

tcp每次发包后,对端需要回ACK确认包

反面教材-高能预警

go的net/http明确提出:

If the Body is not both read to EOF and closed, the Client's underlying RoundTripper (typically Transport) may not be able to re-use a persistent TCP connection to the server for a subsequent "keep-alive" request.

也就是说:httpclient客户端在每次请求结束后,如果不读完body或者没有关闭body, 可能会导致Keep-alive失效,也会导致goroutine泄露。

  1. //  下面的代码没有读完body,导致Keep-alive失效 
  2. func requestWithClose(client *http.Client) { 
  3.    resp, err := client.Get("http://127.0.0.1:8081"
  4.    if err != nil { 
  5.     fmt.Printf("error occurred while fetching page, error: %s", err.Error()) 
  6.     return 
  7.    } 
  8.    defer resp.Body.Close() 
  9.    //_, err = ioutil.ReadAll(resp.Body) 
  10.    fmt.Println("ok"

此次服务端日志如下:

上图红框显示客户端持续使用新的随机端口建立了TCP连接。

查看客户端系统建立的tcp连接:

Wireshark抓包结果:

图中红框显示每次HTTP请求/响应 前后均发生了三次握手、四次挥手。

全文梳理

目前已知的httpclient、httpServer均默认开启keep-alive

禁用keep-alive或者keep-alive失效,会导致客户端、服务器频繁建立tcp连接, 可通过 netstat -an | grep {ip} 查看客户机上建立的tcp连接

 

Wireshark抓包, 明确keep-alive和非Keep-alive的抓包效果

 

责任编辑:武晓燕 来源: 精益码农
相关推荐

2022-04-02 09:38:00

CSS3flex布局方式

2022-04-27 09:28:11

HTTPExpires

2019-08-01 07:56:43

安全数据网络安全安全分析

2017-12-30 00:23:10

容器开源

2019-08-20 09:24:54

Python编程语言Java

2018-01-09 20:35:11

Swift编程语言

2017-12-29 10:29:34

HTTP驱动程序

2012-12-31 11:22:58

开源开放

2011-04-11 10:14:09

服务器虚拟化

2020-09-11 10:55:10

useState组件前端

2018-06-13 12:22:37

康普智能楼宇智慧建筑

2021-02-28 09:47:54

软件架构软件开发软件设计

2017-03-23 14:51:21

HTTP缓存CDN缓存

2017-09-29 16:39:33

2017-03-23 15:05:50

HTTP缓存Cookie

2021-02-19 23:08:27

软件测试软件开发

2018-11-23 11:17:24

负载均衡分布式系统架构

2015-08-21 09:56:15

物联网

2016-08-31 17:24:05

大数据分析

2021-11-07 23:41:39

Windows 11Windows微软
点赞
收藏

51CTO技术栈公众号