HTTP、PHP-FPM、与握手协议
HTTP请求的流程梳理
用户输入url如http:www.baidu.com到浏览器,浏览器如chrom需要将其解析为ip地址才知道需要到哪里去访问哪个服务器。浏览器解析DNS步骤如下
- 搜索浏览器自身的dns缓存,这个缓存缓存时间短,缓存数目有限。
- 搜索操作系统的dns缓存
- 读取host文件的dns映射(一般做本地开发映射都是修改这个文件来达到拦截浏览器请求到本地服务器的目的,从而使本地可以成功映射服务器地址)
- 先本地网卡配置里的dns服务器发起域名解析请求,这里好像还有一套运营商的处理流程就不在展开了。
- 下面好像还有一些流程,由于基本不会执行到这一步,一般所以dns运营商的dns服务器都会搞定的。
- 解析失败,以上任何一步成功都会返回一个成功的ip地址
- 浏览器以一个随机的端口享这个ip地址的特定端口(默认80)发起著名的TCP3次握手。关于一个http请求是如何到达nginx服务的流程大致如下:
- 握手完成后的浏览器和服务器就可以愉快地发送http请求了,具体在nginx上流程如下:
PHP-FPM在服务端出来请求中扮演了什么角色
PHP、nginx与CGI协议
对于一个PHP的web程序来说,web服务器(如:nginx)要想与它通信需要通过CGI协议。当一个web请求触达web服务器时,web服务器会创建一个CGI进程,CGI进程将web的请求按照固定的格式进行解析,然后写入标准输入(STDIN)和环境变量中,然后PHP启动的CGI解析器会从标准输入(STDIN)和环境变量中读取http请求的数据,所以$_SERVER才会有数据,然后做出相应的逻辑处理,然后将处理结果放入标准输出(STDOUT),CGI进程从STDOUT中读取响应数据然后传输给浏览器,这样服务端就完成了一次http请求。
上面是CGI的实现流程,但是使用CGI协议的服务器在用户每次访问服务器的时候都需要fork/销毁CGI进程,必然照成多余的系统开销,所以FASTCGI就是为了解决这个问题的。
什么是FastCGI协议
FastCGI会创建一个常驻的master进程和多个worker进程,master进程负责管理和为worker进程反派任务,worker进程负责内部嵌入了CGI解析器用于解释php代码。
PHP-FPM是一个FastCGI进程管理器,在LNMP体系中就是由它来实现FastCGI协议的。同样,它也会创建一个常驻的master进程和多个worker进程,master进程负责监听端口和接收来自nginx的请求,指派任务给worker进程。worker进程的负责解释php代码。PHP-FPM可以通过配置预先启动一定数量的worker进程,这样当http请求触达时就可以更快速的响应。
Nginx关于FastCGI的配置
nginx与PHP-FPM之间的通信一般通过其ngx_http_fastcgi_module模块来实现。其中fastcgi_pass
用于设置fastcgi服务器的IP地址;fastcgi_param
设置传入fastcgi服务器的参数。这个模块出现的配置问题一般集中在这一块。
相对于并发状态下出现的问题,一般也都集中在fastcgi服务器上,具体表现为fastcgi服务器为了应对大量的http请求必须不停的fork新的worker进程,这时就需要考虑服务器可支持的最大链接数和最大打开文件数(可通过ulimit -n
查看)以及php-fpm配置里的最低开启worker数和最高开启worker数的限制。高性能服务器可以在这个方向上调优。
HTTP协议三次握手四次挥手的细节
协议过程中客服端与服务端的状态图
TCP的标志位说明
标志位 | 英文 | 说明 |
---|---|---|
SYN | synchronous | 建立联机 |
ACK | acknowledgement | 确认 |
PSH | push | 传送 |
FIN | finish | 结束 |
RST | reset | 重置 |
URG | urgent | 紧急 |
Sequence numbe | - | 顺序号码 |
Acknowledge number | - | 确认号码 |
TCP状态说明
状态 | 说明 |
---|---|
LISTEN | 侦听状态 |
SYN_SEND | 发送连接请求[SYN=J]后等待匹配连接请求 |
SYN_RECEIVED | 收到连接请求[SYN=J]后发送连接确认包[SYN=k,ack=J+1]后等待收到确认包[Ack=k+1]状态 |
ESTABLISHED | 打开连接后,可以开始传输数据 |
FIN_WAIT_1 | 发起连接中断请求[FIN=M]后等待远程TCP确认时[Ack=M+1]状态 |
FIN_WAIT_2 | 收到远程中断确认[Ack=M+1]后,等待远程中断请求[FIN=N] |
CLOSE_WAIT | 收到连接中断请求[FIN=M]后未发送出中断确认包[Ack=M=1]状态 |
TIME_WAIT | 发送确认远程中断请求[Ack=N+1]包后,进入等待状态,用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制 |
大量TIME_WAIT的原因「常见性能异常」
在四次挥手断开连接中,发起socket主动关闭的一方 socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),TIME_WAIT状态下的socket不能被回收使用.
具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务.
TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。一般产生的原因是系统没有主动关闭连接,如mysql连接资源没有关闭
关于网络链路中追踪异常用到的运维命令
(以下显示的IP和端口均为假数据)
Linux中查看socket的状态
cat /proc/net/sockstat
参数 | 说明 | ||
---|---|---|---|
sockets:used | 已使用的所有协议套接字总量 | ||
TCP:inuse | 正在使用(正在侦听)的TCP套接字数量。其值≤ netstat –lnt | grep ^tcp | wc –l |
TCP:orphan | 无主(不属于任何进程)的TCP连接数(无用、待销毁的TCP socket数) | ||
TCP:tw | 等待关闭的TCP连接数。其值等于netstat –ant | grep TIME_WAIT | wc –l |
TCP:alloc | 已分配(已建立、已申请到sk_buff)的TCP套接字数量。其值等于netstat –ant | grep ^tcp | wc –l |
TCP:mem | 套接字缓冲区使用量 | ||
UDP:inuse | 正在使用的UDP套接字数量 | ||
FRAG | 使用的IP段数量 |
查看当前tcp链接情况
netstat -na | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
参数 | 说明 |
---|---|
LISTEN | 正在监听状态 |
CLOSE_WAIT | 对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭 |
ESTABLISHED | 建立连接,正在通信 |
TIME_WAIT | 我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT |
tcp工具抓取网络请求包
tcpdump -n port 3306
mysql 主动断开链接
11:38:45.693382 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [F.], seq 123, ack 144, win 227, options [nop,nop,TS val 3000355 ecr 2997359], length 0 # MySQL发送fin包给我
11:38:45.740958 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 124, win 229, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # 我回复ack给它
11:38:45.740960 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [F.], ack 125, win 231, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # MySQL发送fin包给客户端
11:38:45.740965 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 125, win 229, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # 客户端回复ack给我
......
src > dst: flags data-seqno ack window urgent options # 发生了 3次握手 11:38:15.679863 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [S], seq 4065722321, win 29200, options [mss 1460,sackOK,TS val 2997352 ecr 0,nop,wscale 7], length 0 11:38:15.679923 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [S.], seq 780487619, ack 4065722322, win 28960, options [mss 1460,sackOK,TS val 2997352 ecr 2997352,nop,wscale 7], length 0 11:38:15.679936 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 1, win 229, options [nop,nop,TS val 2997352 ecr 2997352], length 0
参数 | 说明 |
---|---|
src > dst | 表明从源地址到目的地址 |
flags | 是TCP包中的标志信息,S 是SYN标志, F(FIN), P(PUSH) , R(RST) "."(没有标记) |
data-seqno | 是数据包中的数据的顺序号 |
ack | 是下次期望的顺序号 |
window | 是接收缓存的窗口大小 |
urgent | 表明数据包中是否有紧急指针 |
options | 是选项 |