网络基础
tcp_no_delay nagel算法和TCP delay ack参数 造成 延时40ms
在特定场景下, Nagel算法要求网络中只有一个未确认的包, 而delay ack机制需要等待更多的数据包, 再发送ACK回包, 导致发送和接收端等待对方发送数据, 造成死锁, 只有当delay ack超时后才能解开死锁,进而导致应用侧对外的延时高
关闭nagel算法,即设置socket tcp_no_delay属性。
static void _set_tcp_nodelay(int fd) {
int enable = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable));
}
避免多次写, 再读取的场景, 合并成一个大包的写;避免一次请求分成多个包发送, 最开始发送的包小于一个MSS,对我们的场景, 把第22号包的1424个字节缓存起来, 大于一个MSS的时候,再发送出去, 服务端立即返回响应, 客户端继续发送后续的数据, 完成交互,避免时延。应该使用一次大数据的写操作,而不是多次小数据的写操作。
三次握手:
SYN_SENT->ESTABLISHED
LISTEN->SYN_RCVD->ESTAVBLISHED
洪水攻击:
syn_ack_retry_size=0
调大 max_syn_backlog 应对少量洪水攻击
CLOSED 表示socket连接没被使用。 LISTENING 表示正在监听进入的连接。 SYN_SENT 表示正在试着建立连接。 SYN_RECEIVED 进行连接初始同步。 ESTABLISHED 表示连接已被建立。 CLOSE_WAIT 表示远程计算器关闭连接,正在等待socket连接的关闭。 FIN_WAIT_1 表示socket连接关闭,正在关闭连接。 CLOSING 先关闭本地socket连接,然后关闭远程socket连接,最后等待确认信息。 LAST_ACK 远程计算器关闭后,等待确认信号。 FIN_WAIT_2 socket连接关闭后,等待来自远程计算器的关闭信号。 TIME_WAIT 连接关闭后,等待远程计算器关闭重发。
CLOSE_WAIT ()表示远程计算器关闭连接,正在等待socket连接的关闭
原因:
确实没有调用close
有耗时操作(火焰图可以非常明显看到),导致超时了
mysql的事务没有正确处理,例如:rollback 或者 commit
if nilMap == nil { o.Rollback()// 这里进行回滚 return false}
造成影响:
socket 资源被不断打满
通常来说,一个CLOSE_WAIT会维持至少2个小时的时间(这个时间外网服务器通常会做调整,要不然太危险了)。如果有个流氓特地写了个程序,给你造成一堆的CLOSE_WAIT,消耗
你的资源,那么通常是等不到释放那一刻,系统就已经解决崩溃了。
解决方法:
只能通过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个问题。
但是实际上,还是主要是因为我们的程序代码有问题,通常是如下问题:
大量time_wait,加快time_wait状态回收
tw_reuse
tw_sycle
大量close_wait,客户端没有正确关闭链接
产生原因
通过图上,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢?
在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。
通常来讲,CLOSE_WAIT状态的持续时间应该很短,正如SYN_RCVD状态。但是在一些特殊情况下,就会出现连接长时间处于CLOSE_WAIT状态的情况。
出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno,如果不是AGAIN,就断开连接。
2.time_wait状态产生的原因
1)为实现TCP全双工连接的可靠释放
由TCP状态变迁图可知,假设发起主动关闭的一方(client)最后发送的ACK在网络中丢失,由于TCP协议的重传机制,执行被动关闭的一方(server)将会重发其FIN,在该FIN到达client之前,client必须维护这条连接状态,也就说这条TCP连接所对应的资源(client方的local_ip,local_port)不能被立即释放或重新分配,直到另一方重发的FIN达到之后,client重发ACK后,经过2MSL时间周期没有再收到另一方的FIN之后,该TCP连接才能恢复初始的CLOSED状态。如果主动关闭一方不维护这样一个TIME_WAIT状态,那么当被动关闭一方重发的FIN到达时,主动关闭一方的TCP传输层会用RST包响应对方,这会被对方认为是有错误发生,然而这事实上只是正常的关闭连接过程,并非异常。
2)为使旧的数据包在网络因过期而消失
为说明这个问题,我们先假设TCP协议中不存在TIME_WAIT状态的限制,再假设当前有一条TCP连接:(local_ip, local_port, remote_ip,remote_port),因某些原因,我们先关闭,接着很快以相同的四元组建立一条新连接。本文前面介绍过,TCP连接由四元组唯一标识,因此,在我们假设的情况中,TCP协议栈是无法区分前后两条TCP连接的不同的,在它看来,这根本就是同一条连接,中间先释放再建立的过程对其来说是“感知”不到的。这样就可能发生这样的情况:前一条TCP连接由local peer发送的数据到达remote peer后,会被该remot peer的TCP传输层当做当前TCP连接的正常数据接收并向上传递至应用层(而事实上,在我们假设的场景下,这些旧数据到达remote peer前,旧连接已断开且一条由相同四元组构成的新TCP连接已建立,因此,这些旧数据是不应该被向上传递至应用层的),从而引起数据错乱进而导致各种无法预知的诡异现象。作为一种可靠的传输协议,TCP必须在协议层面考虑并避免这种情况的发生,这正是TIME_WAIT状态存在的第2个原因。
3)总结
具体而言,local peer主动调用close后,此时的TCP连接进入TIME_WAIT状态,处于该状态下的TCP连接不能立即以同样的四元组建立新连接,即发起active close的那方占用的local port在TIME_WAIT期间不能再被重新分配。由于TIME_WAIT状态持续时间为2MSL,这样保证了旧TCP连接双工链路中的旧数据包均因过期(超过MSL)而消失,此后,就可以用相同的四元组建立一条新连接而不会发生前后两次连接数据错乱的情况。
3.time_wait状态如何避免
首先服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口。在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态。
time wait 后果
由于过于频繁的创建连接,导致系统连接数量被占用,不能及时释放。
现象:
1、外部机器不能正常连接SSH
2、内向外不能够正常的ping通过,域名也不能正常解析。
发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决,
vi /etc/sysctl.conf
编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
然后执行 /sbin/sysctl -p 让参数生效。
https://blog.csdn.net/qq_36520235/article/details/82894126
从输入网址到获得页面的网络请求的过程
(1)域名解析,其实就是根据用户输入的网址去寻找它对应的IP地址,比如输入www.baidu.com的网址就会经历以下过程
1.先从浏览器缓存里找IP,因为浏览器会缓存DNS记录一段时间
2.如没找到,再从Hosts文件查找是否有该域名和对应IP
3.如没找到,再从路由器缓存找
(4)如果还是没有,这个请求就会被发送到ISP(注:Internet Service Provider,互联网服务提供商,就是网络运营商,中国电信中国移动等),ISP也会有相应的ISP DNS服务器,就是本地DNS服务器,请求的域名基本上都能在这里找得到。
4.如没好到,再从DNS缓存查找
5.如果都没找到,浏览器域名服务器向根域名服务器(baidu.com)查找域名对应IP,还没找到就把请求转发到下一级,直到找到IP
(6)如果到了这里还是找不到域名的对应信息,那只能说明一个问题:这个域名本来就不存在,它没有在网上正式注册过。或者域名过期了。
这也就是为什么有时候打开一个新页面会有点慢,因为如果本地没什么缓存,查找域名的过程要这样递归地查询下去,查找完还要一层层的向上返回。例如"mp3.baidu.com",域名先是解析出这是个.com的域名,然后跑到管理.com域的服务器上进行进一步查询,然后是.baidu,最后是mp3, 所以域名结构为:三级域名.二级域名.一级域名。
所以DNS根据域名查询IP地址的过程为:浏览器缓存 --> 操作系统缓存 --> 路由器缓存-->本地(ISP)域名服务器缓存 --> 根域名服务器。
(2)建立TCP连接 (这里使用五层协议更详细的描述如何建立这个TCP链接的)
1. 使用应用层发起HTTP请求(这个可以根据你本身输入的url访问时,用的什么协议就发起对应协议去进行请求)
2. 然后是传输层的TCP协议为传输报文提供可靠的字节流服务,这里也就使用了TCP三次握手
3. 网络层是把TCP分割好的各种数据包传送给接收方。而要保证确实能传到接收方还需要接收方的MAC地址,也就是物理地址
4. 然后才是链路层将数据发送到数据链路层传输。至此请求报文已发出,客户端发送请求的阶段结束
然后是服务端接受请求处理阶段:
原路进行处理:链路层—>网络层—>传输层—>应用层然后响应客户端发送报文。
(3)根据SpringMVC后台业务返回数据,并把数据填充到HTML页面上,然后返回给浏览器
6.关闭TCP连接
当数据完成请求到返回的过程之后,根据Connection的Keep-Alive属性可以选择是否断开TCP连接,HTTP/1.1一般支持同一个TCP多个请求,而不是1.0版本下的完成一次请求就发生断开。TCP的断开与连接不一样,断开可以分为主动关闭和被动关闭,需要经过4次握手。
一次完整的 HTTP 请求解析笔记:
- 经过 DNS 协议映射到 IP 地址
- 经过 TCP 协议分割,按序号可靠发出
- 经过 IP 协议,通过路由器到对应接收的 IP 地址
- 经过 TCP 协议接收分割过得报文段,重组
- 经过 HTTP 协议,返回服务器处理报文后的资源
网友评论