客户端禁用Keep-Alive, 服务端开启Keep-Alive,会怎么样?

aa2e2d89b97d99f38d11e03317580e65.gif

最近部署的web程序,服务器上出现不少time_wait的tcp连接状态,占用了tcp端口,花费几天时间排查。

       之前我有结论:HTTP keep-alive 是在应用层对TCP连接的滑动续约复用,如果客户端、服务器稳定续约,就成了名副其实的长连接

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

HTTP1.1 Keep-Alive到底算不算长连接?

目前所有的HTTP网络库(不论是客户端、服务端)都默认开启了HTTP Keep-Alive,通过Request/Response的Connection标头来协商复用连接。

01

非常规的行为形成的短连接

        我手上有个项目,由于历史原因,客户端禁用了Keep-Alive,服务端默认开启了Keep-Alive,如此一来协商复用连接失败, 客户端每次请求会使用新的TCP连接, 也就是回退为短连接。

d0b3b57f59db06abc38e2924bba22567.png

客户端强制禁用Keep-Alive

package main
import ("fmt""io/ioutil""log""net/http""time"
)func main() {tr := http.Transport{DisableKeepAlives: true,}client := &http.Client{Timeout:   10 * time.Second,Transport: &tr,}for {requestWithClose(client)time.Sleep(time.Second * 1)}
}func requestWithClose(client *http.Client) {resp, err := client.Get("http://10.100.219.9:8081")if err != nil {fmt.Printf("error occurred while fetching page, error: %s", err.Error())return}defer resp.Body.Close()c, err := ioutil.ReadAll(resp.Body)if err != nil {log.Fatalf("Couldn't parse response body. %+v", err)}fmt.Println(string(c))
}

web服务端默认开启Keep-Alive

package mainimport ("fmt""log""net/http"
)// 根据RemoteAddr 知道客户端使用的持久连接
func IndexHandler(w http.ResponseWriter, r *http.Request) {fmt.Println("receive a request from:", r.RemoteAddr, r.Header)w.Write([]byte("ok"))
}func main() {fmt.Printf("Starting server at port 8081\n")// net/http 默认开启持久连接if err := http.ListenAndServe(":8081", http.HandlerFunc(IndexHandler)); err != nil {log.Fatal(err)}
}

从服务端的日志看,确实是短连接。

receive a request from: 10.22.34.48:54722 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54724 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54726 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54728 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54731 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54733 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54734 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54738 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54740 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54741 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54743 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54744 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]
receive a request from: 10.22.34.48:54746 map[Accept-Encoding:[gzip] Connection:[close] User-Agent:[Go-http-client/1.1]]

02

谁是主动断开方?

我想当然的以为 客户端是主动断开方,被现实啪啪打脸。

某一天服务器上超过300的time_wait报警,告诉我这tmd是服务器主动终断连接。

常规的TCP4次挥手, 主动断开方会进入time_wait状态,等待2MSL后释放占用的SOCKET

d7fb67168330be13a0caa7323558d3c2.png

以下是从服务器上tcpdump抓取的tcp连接信息。

61da5640a79682f0ac94860b9b31a9c4.png

2,3红框显示:

      Server端先发起TCP的FIN消息, 之后Client回应ACK确认收到Server的关闭通知; 之后Client再发FIN消息,告知现在可以关闭了, Server端最后发ACK确认收到,并进入time_wait状态,等待2MSL的时间关闭Socket。

特意指出,红框1表示TCP双端同时关闭[1],此时会在Client,Server同时留下time_wait痕迹,发生概率较小。

03

没有源码说个串串

此种情况是服务端主动关闭,我们翻一翻golang httpServer的源码

•http.ListenAndServe(":8081")•server.ListenAndServe()•srv.Serve(ln)•go c.serve(connCtx) 使用go协程来处理每个请求

服务器连接处理请求的简略源码如下:

func (c *conn) serve(ctx context.Context) {c.remoteAddr = c.rwc.RemoteAddr().String()ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())defer func() {if !c.hijacked() {c.close()   // go协程conn处理请求的协程退出时,主动关闭底层的TCP连接c.setState(c.rwc, StateClosed, runHooks)}}()......// HTTP/1.x from here on.ctx, cancelCtx := context.WithCancel(ctx)c.cancelCtx = cancelCtxdefer cancelCtx()c.r = &connReader{conn: c}c.bufr = newBufioReader(c.r)c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)for {w, err := c.readRequest(ctx).....        serverHandler{c.server}.ServeHTTP(w, w.req)w.cancelCtx()if c.hijacked() {return}w.finishRequest()if !w.shouldReuseConnection() {if w.requestBodyLimitHit || w.closedRequestBodyEarly() {c.closeWriteAndWait()}return}c.setState(c.rwc, StateIdle, runHooks)c.curReq.Store((*response)(nil))if !w.conn.server.doKeepAlives() {// We're in shutdown mode. We might've replied// to the user without "Connection: close" and// they might think they can send another// request, but such is life with HTTP/1.1.return}if d := c.server.idleTimeout(); d != 0 {c.rwc.SetReadDeadline(time.Now().Add(d))if _, err := c.bufr.Peek(4); err != nil {return}}c.rwc.SetReadDeadline(time.Time{})}
}

我们需要关注

①for循环,表示尝试复用该conn,用于处理迎面而来的请求②w.shouldReuseConnection() = false, 表明读取到ClientConnection:Close标头,设置closeAfterReply=true,跳出for循环,协程即将结束,结束之前执行defer函数,defer函数内close该连接  c.close()
......
// Close the connection.
func (c *conn) close() {
 c.finalFlush()
 c.rwc.Close()
}
③如果 w.shouldReuseConnection() = true,则将该连接状态置为idle, 并继续走for循环,复用连接,处理后续请求。

04

我的收获

1. TCP 4次挥手的八股文2.  短连接的效应:主动关闭方会在机器上产生 time_wait状态,需要等待2MSL时间才会关闭SOCKET3.golang http keep-alive复用tcp连接的源码级分析4.tcpdump抓包的姿势

引用链接

[1] TCP双端同时关闭: https://blog.csdn.net/q1007729991/article/details/69950255

有态度的马甲建立了真● 高质量交流群:大佬汇聚、无事静默、有事激活、深度思考。

559df74d508d84fe559e893e53d15542.png

[长按图片加我好友]

20327fccbd2a7d41af1eb9c42b8b018a.gif

年终总结:2021技术文大盘点  |  打包过去,面向未来

项目总结:麻雀虽小,五脏俱全

理念总结:实话实说:只会.NET,会让我们一直处于鄙视链、食物链的下游

云原生系列: 什么是云原生?

点“67f4ed76f24d4d6dcf92e2d8214eb2e1.gif戳“在看e0a925e6548c8d5f6435b2a996977cc6.gif

体现态度很有必要!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux网络编程之一般应用采用的协议和不同套接字的地址结构以及用户进程和内核通过哪些函数传递套接字的地址结构

1、一般应用采用的协议 2、不同套接字的地址结构 3、用户进程和内核通过哪些函数传递套接字的地址结构 从进程到内核传递套接字的地址结构函数有3个 bind、connect、sendto函数 从内核到进程传递套接字的地址结构函数有4个函数 accept、recvfrom 、getsockname 、getpeername…

四则运算2测试

这是测试程序在输入任意字符时能否正常运行 1&#xff09;按程序提示正确输入 结果无错 2)当输入错误的字符&#xff0c;如字母等&#xff0c;程序出错&#xff08;错误提示无限循环&#xff09; 这一错误我经过长时间反正为解决&#xff0c;于是请教了其他同学&#xff0c;发现…

2020-11-04关于出现tomcat启动失败的一种原因

点击run on server后出现了如下所示&#xff1a; 本来是运行正常的&#xff0c;后来因为我将exp5里面所有的文件都复制了一遍&#xff0c;放到了exp5_2里面后&#xff0c;如下所示&#xff1a; 此时&#xff08;复制文件夹之前&#xff09;若tomcat已经启动&#xff0c;则不会出…

HTTP协议快速入门

一、定义 The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborative, hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web. Hypertext is structured text that uses logical l…

删除含有关键词的文件_AweEraser——macOS Catalina最佳的文件粉碎机

您是否正在寻找适用于macOS Catalina的好的文件粉碎机&#xff1f;今天macdown为大家推荐一种永久删除数据的软件——AweEraser。有时&#xff0c;你要销毁或擦除计算机上的所有私人文件&#xff0c;这意味着这些数据必须受到保护&#xff0c;免受他人的侵害。本地硬盘或外部硬…

linux网络编程之用一张图片说明函数inet_ntop、inet_pton、inet_addr、inet_ntoa 、inet_aton函数之间的关系

1、inet_ntop、inet_pton、inet_addr、inet_ntoa 、inet_aton函数之间的关系 2、inet_ntop、inet_pton函数的源代码 1、inet_pton函数源码 int inet_pton(int family, const char *strptr, void *addrptr) {if (family == AF_INET) {struct in_addr in_val;if (inet_aton(s…

聊一聊如何用C#轻松完成一个TCC分布式事务

背景 银行跨行转账业务是一个典型分布式事务场景&#xff0c;假设 A 需要跨行转账给 B&#xff0c;那么就涉及两个银行的数据&#xff0c;无法通过一个数据库的本地事务保证转账的 ACID &#xff0c;只能够通过分布式事务来解决。在 聊一聊如何用C#轻松完成一个SAGA分布式事务…

Xcode6.1 模拟器路径

Xcode 5的iOS模拟器的应用的目录是在~/Library/Application Support/iPhone Simulator/<iOS_Version>/Applications/{Application_ID} Xcode 6的目录改为~/Library/Developer/CoreSimulator/Devices/{Device_ID}/data/Containers/Bundle/Application/{Application_ID}/这…

新年伊始 .Net7 preview1 发布!

虎年伊始&#xff0c;.NET 7.0就要来了&#xff0c;还学的动吗&#xff1f;从github能看到&#xff0c;截止到2月8号&#xff0c;.NET 7.0 Preview1已经全部开发完成&#xff0c;连Preview2也完成了85%&#xff0c;这进度杠杠的&#xff01;微软这几年大力推进.NET稳定更新&…

数据挖掘课程实验(8个实验报告)

是从实验一到实验八的 链接&#xff1a;https://download.csdn.net/download/qq_44872173/15558967

hutol json null值没了_JSON数据处理框架Jackson精解第一篇-序列化与反序列化核心用法...

Jackson是Spring Boot默认的JSON数据处理框架&#xff0c;但是其并不依赖于任何的Spring 库。有的小伙伴以为Jackson只能在Spring框架内使用&#xff0c;其实不是的&#xff0c;没有这种限制。它提供了很多的JSON数据处理方法、注解&#xff0c;也包括流式API、树模型、数据绑定…

Linux网络操作系统实验报告(1~12)

共12个 链接在此&#xff1a;https://download.csdn.net/download/qq_44872173/15559247 如下是目录&#xff1a; 实验一部分标题如下&#xff1a;

解读WPF中的Binding

1.Overview基于MVVM实现一段绑定大伙都不陌生&#xff0c;Binding是wpf整个体系中最核心的对象之一这里就来解读一下我花了纯两周时间有哪些秘密。这里我先提出几个问题应该是大家感兴趣的&#xff0c;如下&#xff1a;&#xff08;1&#xff09;INotifyPropertyChanged是如何被…

maven 导入数据库

2019独角兽企业重金招聘Python工程师标准>>> 一.mysql 配置 基本代码 1. pom.xml 文件配置:jeesite.property jdbc.typemysql jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/yaoshi?useUnicodetrue&characterEncodingutf-8 jdbc.user…

下载matlab安装包太慢_MATLAB 2020a商业数学中文版软件下载安装教程

【软件语言】&#xff1a;简体中文 【支持系统】&#xff1a;Win7/Win8/Win10【软件类别】&#xff1a;安装版【更新时间】&#xff1a;2020年5月15日【下载地址】&#xff1a;www.rjazbs.me/t-2945.html客服微信&#xff1a;rjazbsMATLAB是一款商业数学软件&#xff0c;用于算…

phpstorm+Xdebug断点调试PHP

运行环境&#xff1a; PHPSTORM版本 : 8.0.1 PHP版本 : 5.6.2 xdebug版本&#xff1a;php_xdebug-2.2.5-5.6-vc11-x86_64.dll ps : php版本和xdebug版本一定要相对应 1. PHP安装xdebug扩展 php.ini的配置&#xff0c;下面的配置仅供参考&#xff0c;路径要换成自己的&#xff0…

EF Core 6 新功能汇总(三)

在这篇文章中&#xff0c;我将重点介绍 EF Core 6 中 LINQ 查询功能的增强。这是 EF Core 6 新功能汇总的第三篇文章&#xff1a;EF Core 6 新功能汇总&#xff08;一&#xff09;EF Core 6 新功能汇总&#xff08;二&#xff09;EF Core 6 新功能汇总&#xff08;三&#xff0…

SpringMvc项目中使用GoogleKaptcha 生成验证码

前言&#xff1a;google captcha 是google生成验证码的一个工具类&#xff0c;其原理是将随机生成字符串保存到session中&#xff0c;同时以图片的形式返回给页面&#xff0c;之后前台页面提交到后台进行对比。 1、jar包准备 官方提供的pom应该是 <dependency> <grou…