PHP完整请求流程

如有侵权,请联系删除!

域名解析 —> 与web服务器建立连接 —> 发起HTTP请求 —> 服务器响应HTTP请求
—> 如果是动态请求,通过fatcgi_pass将请求交给php-fpm —> php-fpm选择连接一个子进程CGI解释器,将标准输入发送到FastCGI子进程 —> FastCGI的wrapper接收php-fpm转过来的请求,fork一个线程调用PHP解释服务器 —> PHP处理之后的结果返回给web服务器 —> web服务器将结果返回给用户 —> 浏览器解析html代码,并请求html代码中的资源 —> 浏览器对页面进行渲染呈现给用户

域名解析

浏览器输入网址www.***.com,浏览器只知道是一个URL,于是打开地址簿进行查询,一般使用DNS进行查找,还可以使用更精确的地址簿协议HTTPDNS,最终都会得到一个IP地址。

服务器建立连接

拿到域名对应的IP地址之后,User-Agent(一般指浏览器)会以一个随机端口(1024 < 端口< 65535)向服务器的WEB程序(常用的有httpd,nginx)等的80端口。这个连接请求(原始的http请求经过TCP/IP4层模型的层层封包)到达服务器端后(这中间有各种路由设备,局域网内除外),进入到网卡,然后是进入到内核的TCP/IP协议栈(用于识别连接请求,解封包,一层一层的剥开),还有可能要经过Netfilter防火墙(属于内核的模块)的过滤,最终达到WEB程序,最终建立了TCP/IP的连接。

TCP协议:一种面向连接的协议,在互通之前,面向连接的协议会先建立连接。所谓的建立连接,是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性

TCP包头格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EG2lOPdG-1607047537770)(3C54E032A54045D9B053ED065ED3A0A5)]

TCP协议特点:

  1. 顺序问题,稳重不乱。
  2. 丢包问题,承诺靠谱。
  3. 连接维护,有始有终。
  4. 流量控制,把握分寸。
  5. 拥塞控制,知进知退。

从TCP包头格式分析TCP协议特点:

  1. 必不可少的源端口号和目标端口号。
  2. 包序号,解决包乱序问题。
  3. 确认序号,发出去的包需要确认对方有没有收到,没有则重发,保证不丢包。
  4. 状态位,SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。
  5. 窗口大小,TCP 要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力。

建立连接的三次握手:“请求 -> 应答 -> 应答之应答”。一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于LISTEN 状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它
也一发一收了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NG0t73KY-1607047537772)(4CE2BFC032834F55A076A1DC60085EA8)]

断开连接的四次挥手

  1. 断开的时候,A发送断开连接的信息,就进入 FIN_WAIT_1 的状态。
  2. B 收到A的消息后,发送应答信息,就进入 CLOSE_WAIT 的状态。
  3. A 收到B的应答消息后,就进入FIN_WAIT_2的状态。如果这个时候B直接断开,则A将永远在这个状态。TCP协议里面并没有对这个状态的处理,但是Linux有,可以调整tcp_fin_timeout这个参数,设置一个超时时间。
  4. B 向A发送断开连接的信息,A收到信息之后,从FIN_WAIT_2状态结束。如果此时A断开连接,B没有收到A的ACK请求,B则会重新发送断开连接的请求,那么B就再也收不到A的ACK请求了,所以TCP协议要求A最后等待一段时间 TIME_WAIT。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HeqjrQO7-1607047537774)(A91E07841E10405CAEA65F22AD171C15)]

UDP协议:面向无连接,继承了IP的特性,基于数据报的,一个一个地发,一个一个地收;不保证不丢失,不保证按顺序到达;无论网络多么拥挤都不停止发送;无状态服务。它不需要所谓的握手操作,从而加快了通信速度,允许网络上的其他主机在接收方同意通信之前进行数据传输。

UDP包头格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qefdEPZf-1607047537776)(9ED3EC5886AC47838EFC157F4B633DE9)]

UDP使用场景:

  1. 需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用。
  2. 不需要一对一沟通,建立连接,而是可以广播的应用。
  3. 需要处理速度快,时延低,可以容忍少数丢包,但是要求即便网络拥塞,也毫不退
    缩,一往无前的时候。

发起HTTP请求

得到目标地址,建立连接后,浏览器开始打包请求,普通请求一般会使用HTTP协议;对于支付等请求,需要加密传输,往往使用HTTPS协议。

1. 对称加密:加密和解密采用相同的密钥,是可逆的(即可解密)。
   优点:加密速度快。
   缺点:密钥的传输及保存存在安全问题,参与加密解密的密钥都是一样的,很容易造成泄露。
2. 非对称加密:加密和解密采用不同的密钥(公钥和私钥)。
   优点:加密和解密的密钥不一致,公钥是可以公开的,只需保证私钥不被泄露即可,这样就密钥的传递变的简单很多,从而降低了被破解的几率。
   缺点:加密速度慢。
3. HTTPS工作模式:公钥私钥主要用于传输对称加密的秘钥,而真正的双方大数据量的通信通过对称加密进行。
HTTP请求格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZmN66tZ-1607047537777)(31B8664A6EBF4E5B8FD6BB86AD070E84)]

请求行

  • 方法:
    1. GET:从服务器获取资源;
    2. POST:主动告诉服务端信息;
    3. PUT:向指定资源位置上传最新内容;
    4. DELETE:删除资源;
  • URL:http://www.***.com
  • 版本:HTTP1.1/HTTP2.0

首部:首部字段就是 key : value

  • Accept-Charset:客户端可以接受的字符集,防止传过来的是另外的字符集,从而导致出现乱码。
  • Content-Type:正文格式,设置请求时正文的格式。
  • Cookie:缓存,在真正的业务逻辑之前,都需要有个接入层,将这些静态资源的请求拦在最外面,避免请求占用大量资源。
  • Cache-control:控制缓存,当客户端发送的请求中包含max-age指令时,如果判定缓存层中,资源的缓存时间数值比指定时间的数值小,那么客户端可以接受缓存的资源;当指定 max-age 值为 0,那么缓存层通常需要将请求转发给应用集群。

HTTP发送请求

  1. HTTP协议基于TCP协议,使用面向连接的方式发送请求,通过stream二进制流的方式传给对方。到了 TCP 层,它会把二进制流变成一个的报文段发送给服务器。
  2. 在发送每段报文的时候,都需要对方发一个回应ACK来保证报文是否到达对方。如果没有回应,TCP会重新进行传输,直到到达。所以同一个包有可能被传输很多次,但是只是TCP层一直在运作,HTTP不需要做任何处理。
  3. TCP 层发送每一个报文的时候,都需要加上自己的地址(即源地址)和它想要去的地方(即目标地址),将这两个信息放到 IP 头里面,交给 IP 层进行传输。
  4. IP 层需要查看目标地址和自己是否是在同一个局域网。如果是,就发送 ARP 协议来请求这个目标地址对应的 MAC 地址,然后将源 MAC 和目标 MAC 放入 MAC 头,发送出去即可;如果不在同一个局域网,就需要发送到网关,还要需要发送 ARP 协议,来获取网关的MAC 地址,然后将源 MAC 和网关 MAC 放入 MAC 头,发送出去。
  5. 网关收到包发现MAC符合,取出目标IP地址,根据路由协议找到下一跳的路由器,获取下一跳路由器的 MAC 地址,将包发给下一跳路由器。
  6. 路由器一跳一跳终于到达目标的局域网。这个时候,最后一跳的路由器能够发现,目标地址就在自己的某一个出口的局域网上。于是,在这个局域网上发送 ARP,获得这个目标地址的 MAC 地址,将包发出去。
  7. 目标的机器发现 MAC 地址符合,就将包收起来;发现 IP 地址符合,根据 IP 头中协议项,知道自己上一层是 TCP 协议,于是解析 TCP 的头,里面有序列号,需要看一看这个序列包是不是我要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。
  8. TCP 头里面还有端口号,HTTP 的服务器正在监听这个端口号。于是,目标机器自然知道是
    HTTP 服务器这个进程想要这个包,于是将包发给 HTTP 服务器。HTTP 服务器的进程看
    到,原来这个请求是要访问一个网页,于是就把这个网页发给客户端。

HTTP返回报文:与请求报文略有不同。

HTTP返回格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pK8P3eo3-1607047537779)(AC36640F58E84034A28D28D77CA62D9C)]

状态行

  • 版本:HTTP1.1/HTTP2.0
  • 状态码:状态码会反应 HTTP 请求的结果。

1xx
(Informational) 信息性状态码,表示正在处理。

2xx
(Success) 成功状态码,表示请求正常。

200 ok 请求被成功处理。
204 No Content 该状态码表示服务器接收到的请求已经处理完毕,但是服务器不需要返回响应体。
206 Partial Content 该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。

3xx

(Redirection) 重定向状态码,表示客户端需要进行附加操作。

301 Moved Permanently 永久性重定向。
302 Found 临时性重定向。

4xx

(Client Error) 客户端错误状态码,表示服务器无法处理请求。

400 Bad Request 指出客户端请求中的语法错误。
401 Unauthorized 该状态码表示发送的请求需要有认证。
403 Forbidden 该状态码表明对请求资源的访问被服务器拒绝了。
404 Not Found 该状态码表明服务器上无法找到指定的资源。

5xx

(Server Error) 服务器错误状态码,表示服务器处理请求出错。

500 Internal Server Error 该状态码表明服务器端在执行请求时发生了错误。
502 Bad Gateway 该状态码表明服务器网关错误。
503 Service Unavailable 该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。

  • 短语:大概说一下状态码的原因。

服务器响应HTTP请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V65ZNOLl-1607047537780)(2D36478A0B90404E9D3E57613E19C29A)]

nginx:nginx采用的是多线程(单线程)& IO多路复用模型。使用了 I/O 多路复用技术的Nginx,就成了“并发事件驱动”的服务器。

nginx多进程工作模式

  1. Nginx 在启动后,会有一个 master 进程和多个相互独立的 worker 进程。
  2. 接收来自外界的信号,向各worker进程发送信号,每个进程都有可能来处理这个连接。
  3. master 进程能监控 worker 进程的运行状态,当 worker 进程退出后(异常情况下),会自动启动新的 worker 进程。

master进程:主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。master进程充当整个进程组与用户的交互接口,同时对进程进行监护。它不需要处理网络事件,不负责业务的执行,只会通过管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。

worker进程:而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。

worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

nginx惊群现象:主进程(master 进程)首先通过 socket() 来创建一个 sock 文件描述符用来监听,然后fork生成子进程(workers 进程),子进程将继承父进程的 sockfd(socket 文件描述符),之后子进程 accept() 后将创建已连接描述符(connected descriptor)),然后通过已连接描述符来与客户端通信。

nginx对惊群现象处理:nginx提供accept_mutex,这是一个加在accept上的一把共享锁。即每个 worker 进程在执行 accept 之前都需要先获取锁,获取不到就放弃执行 accept()。有了这把锁之后,同一时刻,就只会有一个进程去 accpet(),这样就不会有惊群问题了。accept_mutex 是一个可控选项,我们可以显示地关掉,默认是打开的。

worker进程工作流程:当一个 worker 进程在 accept() 这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,一个完整的请求。一个请求,完全由 worker 进程来处理,而且只能在一个 worker 进程中处理。这样带来的好处:1.节省锁带来的开销。每个 worker 进程都是独立的进程,不共享资源,不需要加锁。同时在编程以及问题查上时,也会方便很多。2.独立进程,减少风险。采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master 进程则很快重新启动新的 worker 进程。当然,worker 进程的也能发生意外退出。

多进程模型每个进程/线程只能处理一路IO,在一个进程中,同时只能处理一个请求,比如执行 accept(),如果没有连接过来,那么程序会阻塞在这里,直到有一个连接过来,才能继续向下执行。而使用多路复用,允许我们只在事件发生时才将控制返回给程序,而其他时候内核都挂起进程,随时待命。

IO多路复用:nginx采用IO多路复用模型epoll,epoll 模型就是 Linux 操作系统内的一个内核的模块。它是一个较轻的、高效的、处理 IO 复用的一个内核模型。其工作流程分为三部分。

1、调用 int epoll_create(int size)建立一个epoll对象,内核会创建一个eventpoll结构体,用于存放通过epoll_ctl()向epoll对象中添加进来
	的事件,这些事件都会挂载在红黑树中。
2、调用 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 在 epoll 对象中为 fd 注册事件,所有添加到epoll中的件
	都会与设备驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个sockfd的回调方法,将sockfd添加到eventpoll 中的双链表
3、调用 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) 来等待事件的发生,timeout 为 -1 时,该
	调用会阻塞知道有事件发生

注册好事件之后,只要有 fd 上事件发生,epoll_wait() 就能检测到并返回给用户,用户就能”非阻塞“地进行 I/O 了。epoll() 中内核则维护一个链表,epoll_wait 直接检查链表是不是空就知道是否有文件描述符准备好了。(epoll 与 select 相比最大的优点是不会随着 sockfd 数目增长而降低效率,使用 select() 时,内核采用轮训的方法来查看是否有fd 准备好,其中的保存 sockfd 的是类似数组的数据结构 fd_set,key 为 fd,value 为 0 或者 1。)

能达到这种效果,是因为在内核实现中 epoll 是根据每个 sockfd 上面的与设备驱动程序建立起来的回调函数实现的。那么,某个 sockfd 上的事件发生时,与它对应的回调函数就会被调用,来把这个 sockfd 加入链表,其他处于“空闲的”状态的则不会。在这点上,epoll 实现了一个”伪”AIO。但是如果绝大部分的 I/O 都是“活跃的”,每个 socket 使用率很高的话,epoll效率不一定比 select 高。

具体epoll模型可参考 https://blog.csdn.net/zhaobryant/article/details/80557262

可以看出,因为一个进程里只有一个线程,所以一个进程同时只能做一件事,但是可以通过不断地切换来“同时”处理多个请求。基于多进程+epoll模型,nginx便能实现高并发。

例如:Nginx 会注册一个事件:“如果来自一个新客户端的连接请求到来了,再通知我”,此后只有连接请求到来,服务器才会执行 accept() 来接收请求。又比如向上游服务器(比如 PHP-FPM)转发请求,并等待请求返回时,这个处理的 worker 不会在这阻塞,它会在发送完请求后,注册一个事件:“如果缓冲区接收到数据了,告诉我一声,我再将它读进来”,于是进程就空闲下来等待事件发生。

正向代理:正向代理服务器位于客户端和服务器之间,为了向服务器获取数据,客户端要向代理服务器发送一个请求,并指定目标服务器,代理服务器将目标服务器返回的数据转交给客户端。这里客户端是要进行一些正向代理的设置的。

反向代理:反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,在返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NZzHiPb1-1607047537781)(513F6F3467FE4894A81AF802C34C11D7)]

多个客户端给服务器发送的请求,Nginx 服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。此时请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx 扮演的就是一个反向代理角色。客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。

反向代理的作用:保证内网的安全,通常将反向代理作为公网访问地址,Web 服务器是内网。负载均衡,通过反向代理服务器来优化网站的负载。

负载均衡:客户端发送的、Nginx 反向代理服务器接收到的请求数量,就是我们说的负载量。请求数量按照一定的规则进行分发,到不同的服务器处理的规则,就是一种均衡规则。所以将服务器接收到的请求按照规则分发的过程,称为负载均衡

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r79SdBHN-1607047537782)(6CE625C9EB804201974B321872AABA0F)]

负载均衡在实际项目操作过程中,有硬件负载均衡和软件负载均衡两种,硬件负载均衡也称为硬负载,如 F5 负载均衡,相对造价昂贵成本较高。但是数据的稳定性安全性等等有非常好的保障,如中国移动中国联通这样的公司才会选择硬负载进行操作。更多的公司考虑到成本原因,会选择使用软件负载均衡,软件负载均衡是利用现有的技术结合主机硬件实现的一种消息队列分发机制。

nginx负载均衡策略:负载均衡用于从“upstream”模块定义的后端服务器列表中选取一台服务器接受用户的请求。一个最基本的upstream模块是这样的,模块内的server是服务器列表:

#动态服务器组
    upstream backserver  {
        server localhost:8080;  
        server localhost:8081;  
        server localhost:8082; 
        server localhost:8083;  
    }

在upstream模块配置完成后,要让指定的访问反向代理到服务器列表:

        location ~ .*$ {
            index index.htm index.html;
            proxy_pass http://backserver ;
        }

这就是最基本的负载均衡实例,但这不足以满足实际需求;目前Nginx服务器的upstream模块支持6种方式的分配:

参数配置:

参数作用
fail_timeout与max_fails结合使用。
max_fails设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了。
fail_time服务器会被认为停机的时间长度,默认为10s。
backup标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里。
down标记服务器永久停机了。
  1. 轮询(默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。

    upstream backserver { 
        server 192.168.0.14; 
        server 192.168.0.15; 
    } 
  1. 指定权重

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。

    upstream backserver { 
        server 192.168.0.14 weight=8; 
        server 192.168.0.15;
        server 192.168.0.16 backup;
        server 192.168.0.17 max_fails=3 fail_timeout=20s;
    } 
  1. IP绑定 ip_hash

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

    upstream backserver {
        ip_hash;    #保证每个访客固定访问一个后端服务器
        server 192.168.0.14   weight=2;  
        server 192.168.0.15;  
        server 192.168.0.16;  
        server 192.168.0.17   max_fails=3 fail_timeout=20s;  
    }

注意:

  • 在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。
  • ip_hash不能与backup同时使用。
  • 此策略适合有状态服务,比如session。
  • 当有服务器需要剔除,必须手动down掉。
  1. least_conn

把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。

    upstream backserver {
        least_conn;    #把请求转发给连接数较少的后端服务器
        server 192.168.0.14   weight=2;  
        server 192.168.0.15;  
        server 192.168.0.16;  
        server 192.168.0.17   max_fails=3 fail_timeout=20s;  
    }
  1. fair(第三方)

按后端服务器的响应时间来分配请求,响应时间短的优先分配。

    upstream backserver { 
        server 192.168.0.14;  
        server 192.168.0.15;  
        server 192.168.0.16;  
        server 192.168.0.17;  
        fair;    #实现响应时间短的优先分配
    } 
  1. url_hash(第三方)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。

    upstream backserver { 
        hash $request_uri;    #实现每个url定向到同一个后端服务器
        server 192.168.0.14;  
        server 192.168.0.15;  
        server 192.168.0.16;  
        server 192.168.0.17;  
    } 

nginx处理请求

nginx会根据用户访问的URI和后缀对请求进行判断,根据用户请求的资源匹配到具体的location后,会执行location对应的动作。

如果用户请求的是动态内容,nginx会将请求交给fastcgi客户端,通过fastcgi_pass将用户的请求发送给php-fpm,如果用户访问的是静态资源,nginx直接将用户请求的静态资源返回给用户。

#定义Nginx运行的用户和用户组
user www www;

#nginx进程数,建议设置为等于CPU总核心数。
worker_processes 8;

#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log info;

#进程文件
pid /var/run/nginx.pid;

#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。
worker_rlimit_nofile 65535;

#工作模式与连接数上限
events
{
#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll;
#单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 65535;
}

#设定http服务器
http
{
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型
#charset utf-8; #默认编码
server_names_hash_bucket_size 128; #服务器名字的hash表大小
client_header_buffer_size 32k; #上传文件大小限制
large_client_header_buffers 4 64k; #设定请求缓
client_max_body_size 8m; #设定请求缓
sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。
tcp_nopush on; #防止网络阻塞
tcp_nodelay on; #防止网络阻塞
keepalive_timeout 120; #长连接超时时间,单位是秒

#FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;

#gzip模块设置
gzip on;        #开启gzip压缩输出
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)开始压缩的http协议版本(可以不设置,目前几乎全是1.1协议)
gzip_comp_level 2;   #推荐6压缩级别(级别越高,压的越小,越浪费CPU计算资源)
gzip_types text/plain application/x-javascript text/css application/xml;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_vary on;   # 是否传输gzip压缩标志
#limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用

upstream blog.ha97.com {
#upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
}

#虚拟主机的配置
server
{
    #监听端口
    listen 80;
    #域名可以有多个,用空格隔开
    server_name www.ha97.com ha97.com;
    index index.html index.htm index.php;
    root /data/www/ha97;
    location ~ .*\.(php|php5)?$
    {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi.conf;
    }
    #图片缓存时间设置
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
    expires 10d;
    }
    #JS和CSS缓存时间设置
    location ~ .*\.(js|css)?$
    {
    expires 1h;
    }
    #日志格式设定
    log_format access '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" $http_x_forwarded_for';
    #定义本虚拟主机的访问日志
    access_log /var/log/nginx/ha97access.log access;

    #对 "/" 启用反向代理
    location / {
    proxy_pass http://127.0.0.1:88;
    proxy_redirect off;
    proxy_set_header X-Real-IP $remote_addr;
    #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #以下是一些反向代理的配置,可选。
    proxy_set_header Host $host;
    client_max_body_size 10m; #允许客户端请求的最大单文件字节数
    client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数,
    proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
    proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
    proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
    proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
    proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置
    proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
    proxy_temp_file_write_size 64k;
    #设定缓存文件夹大小,大于这个值,将从upstream服务器传
    }

    #设定查看Nginx状态的地址
    location /NginxStatus {
    stub_status on;
    access_log on;
    auth_basic "NginxStatus";
    auth_basic_user_file conf/htpasswd;
    #htpasswd文件的内容可以用apache提供的htpasswd工具来产生。
    }

    #本地动静分离反向代理配置
    #所有jsp的页面均交由tomcat或resin处理
    location ~ .(jsp|jspx|do)?$ {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8080;
    }
    #所有静态文件由nginx直接读取不经过tomcat或resin
    location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$
    { expires 15d; }
    location ~ .*.(js|css)?$
    { expires 1h; }
}
}

PHP-FPM处理请求

CGI:CGI全称是“通用网关接口”(Common Gateway Interface),是外部应用程序(CGI程序)与WEB服务器之间的接口标准,也是WEB服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序一般运行在网络服务器上。 CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,python等。

当web server收到/index.php这个请求后,会启动对应的CGI程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程。web server再把结果返回给浏览器。

调用原理大概为:
用户请求->Web服务器接收请求->fork子进程调用程序/执行程序->程序返回内容/程序调用结束->web服务器接收内容->返回给用户由于每次用户请求,都得fork创建进程调用一次程序,然后销毁进程,所以性能较低。

FastCGI:FastCGI全称 快速通用网关接口(FastCommonGatewayInterface)。fast-cgi是cgi模式的升级版,它像是一个常驻型的cgi,只要开启后,就可一直处理请求,不再需要结束进程

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。

调用原理大概为:
web服务器fast-cgi进程管理器初始化->预先fork n个进程
用户请求->web服务器接收请求->交给fast-cgi进程管理器->fast-cgi进程管理区接收,给其中一个空闲fast-cgi进程处理->处理完成,fast-cgi进程变为空闲状态,等待下次请求->web服务器接收内容->返回给用户

PHP-FPM:php-fpm即php-Fastcgi Process Manager。
php-fpm是 FastCGI 的实现,并提供了进程管理的功能。
进程包含 master 进程和 worker 进程两种进程。
master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

php-fpm功能:

  • 支持平滑停止/启动的高级进程管理功能;
  • 可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);
  • stdout 和 stderr 日志记录;
  • 在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;
  • 文件上传优化支持;
  • “慢日志” - 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;
  • fastcgi_finish_request() - 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);
  • 动态/静态子进程产生;
  • 基本 SAPI 运行状态信息(类似Apache的 mod_status);
  • 基于 php.ini 的配置文件。

调用原理大概为:php-fpm启动->生成n个fast-cgi协议处理进程->监听一个端口等待任务
用户请求->web服务器接收请求->请求转发给php-fpm->php-fpm交给一个空闲进程处理
->进程处理完成->php-fpm返回给web服务器->web服务器接收数据->返回给用户

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值