Nginx + php-fpm工作原理学习

in iOS developing with 0 comment

Nginx + php-fpm工作原理

要明白nginx 与 php-fpm是如何协同工作的,那么首先要明白CGI (Common Gateway Interface) 和 FastCGI 这两个协议

CGI与fastcgi

CGI 是 Web Server 与后台语言交互的协议,有了这个协议,开发者可以使用任何语言处理[比如网上有教程用C来写cgi] Web Server 发来的请求,动态的生成内容。但 CGI 有一个致命的缺点,那就是每处理一个请求都需要 fork 一个全新的进程,随着 Web 的兴起,高并发越来越成为常态,这样低效的方式明显不能满足需求。就这样,FastCGI 诞生了,CGI 很快就退出了历史的舞台。

FastCGI,顾名思义为更快的CGI, 它允许在一个进程内处理多个请求,而不是一个请求处理完毕就直接结束进程,性能上有了很大的提高。

FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等。同时,FastCGI也被许多脚本语言支持,其中就有PHP

FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器[如nginx]专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

至于 FPM (FastCGI Process Manager),它是 FastCGI 的实现,任何实现了 FastCGI 协议的 Web Server 都能够与之通信。FPM 之于标准的 FastCGI,也提供了一些增强功能.

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

Nginx的模块与工作原理

Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端请求映射到一个location block(location是Nginx配置中的一个指令,用于URL匹配),而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。

如nginx.conf,请求过来后nginx会根据.php在conf配置文件中找到对应的location block,交由fastcig处理[php-fpm]

PHP-FPM的默认配置php-fpm.conf

 listen_address  127.0.0.1:9000 #这个表示php的fastcgi进程监听的ip地址以及端口
     start_servers
     min_spare_servers
     max_spare_servers

nginx.conf location段配置

location ~ \.php$ {
            root html;   
            fastcgi_pass 127.0.0.1:9000; 指定了fastcgi进程侦听的端口,nginx就是通过这里与php交互的
            fastcgi_index index.php;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME   /usr/local/nginx/html$fastcgi_script_name;
    }

Nginx通过location指令,将所有以php为后缀的文件都交给127.0.0.1:9000来处理,而这里的IP地址和端口就是FastCGI进程监听的IP地址和端口

Nginx的模块从结构上分为核心模块、基础模块和第三方模块

  1. 核心模块:HTTP模块、EVENT模块和MAIL模块
  2. 基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块
  3. 第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块

用户根据自己的需要开发的模块都属于第三方模块。正是有了这么多模块的支撑,Nginx的功能才会如此强大

Nginx的模块从功能上分为如下三类

  1. Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个
  2. Filters (过滤器模块)。此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出
  3. Proxies (代理类模块)。此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能

Nginx的模块直接被编译进Nginx,因此属于静态编译方式。启动Nginx后,Nginx的模块被自动加载,不像Apache,首先将模块编译为一个so文件,然后在配置文件中指定是否进行加载。在解析配置文件时,Nginx的每个模块都有可能去处理某个请求,但是同一个处理请求只能由一个模块来完成

Nginx的进程模型

在工作方式上,Nginx分为单工作进程和多工作进程两种模式。在单工作进程模式下,除主进程外,还有一个工作进程,工作进程是单线程的;在多工作进程模式下,每个工作进程包含多个线程。Nginx默认为单工作进程模式.

Nginx在启动后,会有一个master进程和多个worker进程

master进程

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这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。

以下为简化图 nginx进程模型

nginx进程模型

Nginx+FastCGI运行原理

Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket(这个socket可以是文件socket,也可以是ip socket)

wrapper:为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接收到请求,然后Fork(派生)出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据(html页面或者图片)发送给客户端

image

这个wrapper需要完成的工作

  1. 调用fastcgi的函数使socket和ningx通信(读写socket是fastcgi内部实现的功能,对wrapper是非透明的)
  2. 调度thread,进行fork和kill
  3. 和application(php)进行通信

Nginx与PHP-FPM

整体工作流程

  1. FastCGI进程管理器php-fpm自身初始化,启动主进程php-fpm和启动start_servers个FastCGI 子进程.主进程php-fpm主要是管理fastcgi子进程,监听9000端口.
  2. 当客户端请求到达Web ServerNginx时,将所有以php为后缀的文件都交给127.0.0.1:9000来处理[见第30行配置]
  3. PHP-FPM选择并连接到一个子进程CGI解释器.Nginx将CGI环境变量和标准输入发送到FastCGI子进程
  4. FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回给Nginx,当FastCGI子进程关闭连接时,请求便告处理完成
  5. FastCGI子进程接着等待并处理来自FastCGI进程管理器[php-fpm]的下一个连接

然后看我就够了

评论