NGINX+PHP运行原理

前向代理与反向代理

前向代理作为客户端的代理,将从互联网上获取的资源返回给一个或多个的客户端,服务器端(如Web服务器)只知道代理的IP地址而不知道客户端的IP地址。

1.png

而反向代理是作为服务器端(如Web服务器)的代理使用,而不是客户端。客户端借由前向代理可以间接访问很多不同互联网服务器(簇)的资源,而反向代理是供很多客户端都通过它间接访问不同后端服务器上的资源,而不需要知道这些后端服务器的存在,而以为所有资源都来自于这个反向代理服务器。

2.png

CGI

通用网关接口(Common Gateway Interface/CGI)是一种重要的互联网技术,可以让一个客户端,从网页浏览器向执行在网络服务器上的进程请求数据。CGI描述了服务器和请求处理进程之间传输数据的一种标准。

CGI进程可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。除Perl外,像Unix shell script, Python, Ruby, PHP, Tcl, C/C++,和Visual Basic都可以用来编写CGI进程。

工作方式
从Web服务器的角度看,是在特定的位置(比如:http://www.example.com/wiki.cgi)定义了可以运行CGI进程。当收到一个匹配URL的请求,相应的进程就会被调用,并将客户端发送的数据作为输入。进程的输出会由Web服务器收集,并加上合适的档头,再发送回客户端。

缺点
一般每次的CGI请求都需要新生成一个进程的副本来运行,这样大的工作量会很快将服务器压垮,因此一些更有效的技术像mod_php,可以让脚本解释器直接作为模块集成在Web服务器(例如:Apache)中,这样就能避免重复载入和初始化解释器。不过这只是就那些需要解释器的高级语言(即解释语言)而言的,使用诸如C一类的编译语言则可以避免这种额外负荷。由于C及其他编译语言的进程与解释语言进程相比,前者的运行速度更快、对操作系统的负荷更小,使用编译语言进程是可能达到更高执行效率的,然而因为开发效率等原因,在目前直译性语言还是最合适的。

FAST-CGI

快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互进程与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。
FastCGI致力于减少网页服务器与CGI进程之间交互的开销,从而使服务器可以同时处理更多的网页请求。

CGI使外部进程与Web服务器之间交互成为可能。CGI进程运行在独立的进程中,并对每个Web请求创建一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量创建和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。

与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI服务器管理,而不是web服务器。 当进来一个请求时,web服务器把环境变量和这个页面请求通过一个socket比如FastCGI进程与web服务器(都位于本地)或者一个TCP connection(FastCGI进程在远端的server farm)传递给FastCGI进程。

PHP-FPM

全称php-Fastcgi Process Manager,是对FastCGI的实现,并提供了进程管理的功能。

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

执行流程

Nginx 不仅仅是一个 Web 服务器,也是一个功能强大的 Proxy 服务器,除了进行 http 请求的代理,也可以进行许多其他协议请求的代理,包括本文与 fpm 相关的 fastcgi 协议。为了能够使 Nginx 理解 fastcgi 协议,Nginx 提供了 fastcgi 模块来将 http 请求映射为对应的 fastcgi 请求。

Nginx 的 fastcgi 模块提供了 fastcgi_param 命令来主要处理这些映射关系,下面 Ubuntu 下 Nginx 的一个配置文档,其主要完成的工作是将 Nginx 中的变量翻译成 PHP 中能够理解的变量。

Nginx 的 fastcgi 模块提供了 fastcgi_param 命令来主要处理这些映射关系,下面 Ubuntu 下 Nginx 的一个配置文档,其主要完成的工作是将 Nginx 中的变量翻译成 PHP 中能够理解的变量。

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

除此之外,非常重要的就是 fastcgi_pass 命令了,这个命令用于指定 fpm 进程监听的地址,Nginx 会把所有的 php 请求翻译成 fastcgi 请求之后再发送到这个地址。下面一个简单的可以工作的 Nginx 配置文档:

server {
    listen       80; #监听80端口,接收http请求
    server_name  www.example.com; #就是网站地址
    root /usr/local/etc/nginx/www/huxintong_admin; # 准备存放代码工程的路径
    #路由到网站根目录www.example.com时候的处理
    location / {
        index index.php; #跳转到www.example.com/index.php
        autoindex on;
    }   

    #当请求网站下php文档的时候,反向代理到php-fpm
    location ~ .php$ {
        include /usr/local/etc/nginx/fastcgi.conf; #加载nginx的fastcgi模块
        fastcgi_intercept_errors on;
        fastcgi_pass   127.0.0.1:9000; #这个命令用于指定 fpm 进程监听的地址,Nginx 会把所有的 php 请求翻译成 fastcgi 请求之后再发送到这个地址
    }
}

具体流程如下:

graph TD; www.example.com-->Nginx Nginx-->路由到www.example.com/index.php 路由到www.example.com/index.php-->加载nginx的fast-cgi模块 加载nginx的fast-cgi模块-->发送到127.0.0.1:9000地址 发送到127.0.0.1:9000地址-->PHP-FPM监听到发送到这个端口的请求 PHP-FPM监听到发送到这个端口的请求-->等待处理

参考链接