从CGI到FastCGI到PHP-FPM

背景

笔者在学习这几个名词的时候,也是被百度到的相关文章迷惑。涉及到的主要名词包括

1. CGI协议
2. CGI脚本
3. PHP-CGI
4. FastCGI协议
5. PHP-FPM

要真正理解这些名词,如果我们硬生生的解释,也很难记住。我们可以从web服务器开发的历程来看,看看他们为什么会出现,以及他们解决了什么问题。

早些年的简单服务器

早些年的服务器很简单,你访问一个网站,比如www.helloworld.com,网站只返回你一个静态HTML页面。为了简单起见,我们假设网站值返回hello标题,这样流程可以用下图表示

这时候server是采用C语言编写的,基本上一个简的C脚本就可以了,把这个服务器脚本命名为hello_server.c,代码可以到github上下载

在Linux环境中使用gcc编译hello_server.c

vagrant@kfz:~$ gcc hello_world.c

然后采用curl工具测试

vagrant@kfz:~$ curl "127.0.0.1:8887"
GET / HTTP/1.1
User-Agent: curl/7.49.1
Host: 127.0.0.1:8887
Accept: */* Hello World

这里我们的重点不在HTTP响应的格式上,所以我们直接输出了Hello World,如果是请求静态页面,我们也是一样的思路,读取静态文件的内容然后返还给客户端。

思考:当Web时代越来越火爆,我们希望Web服务器有更多的功能,比如写博客,聊天,等等。同时,越来越多的不同领域的开发者想在web时代大显身手。很多服务器开发者发现有了以下缺点

#服务器功能方面
Web服务器功能会随着这种逻辑的增长而增长,服务器会变的不专一 #语言支持方便
越来越多的PHP,Python开发者想开发服务端程序,编写更多的功能,发现自己的语言无从下手

CGI协议与CGI程序

什么是CGI协议和CGI程序

既然Web服务器想做的专一,但又要支持Web的飞速发展,同时还想让其他语言开发者也能加入Web开发。所以,就出了一种方式,Web服务器和其他领域的开发者拟定一条协议,在保证遵守协议的基础上,剩下的可以自由发挥。没错,这条协议就是CGI协议,协议的内容可以查看,而实现这个协议的脚本叫做CGI程序。CGI协议规定了需要向CGI脚本设置的环境变量和一些其他信息,CGI程序完成某一个功能,可以用PHP,Python,Shell或者C语言编写。

这样的好处Web开发更加开放了,可以用任何开发者擅长的语言开发Web程序。比如,我是一个PHP开发者,用PHP有很多现成的库访问第三方服务,入MySQL,Memcache等,但是这些第三方服务使用C语言接入还是比较麻烦的。有了CGI协议,我们的Web处理流程可以变成下图这样

实现一个PHP版的CGI程序

一般情况下,我们把CGI程序放在cgi-bin目录下面,新建PHP版CGI处理程序cgi-bin/get_user.php,我们的服务器程序还是使用简单的hello_server.c

目标:用户访问URL127.0.0.1:8887/cgi-bin/get_user.php?id=1的的时候输出用户ID为1的用户信息

实现:很显然,我们要解决两个问题

1. 在服务器程序hello_server.c中执行get_user.php文件
2. 服务器程序hello_server.c向get_user.php中传递请求信息(id=1)

第一个问题我们使用C库还是popen来运行php脚本,第二个问题在CGI协议中规定了我们的解决方案。首先需要将请求参数放在环境变量中,其次,环境变量的名字也规定好了。这里列举一些CGI协议规定的常用的环境变量,这里只列举一些,全部的内容可以协议明细

变量名 描述
CONTENT_TYPE 这个环境变量的值指示所传递来的信息的MIME类型。目前,环境变量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,他表示数据来自于HTML表单
HTTP_USER_AGENT 提供包含了版本数或其他专有数据的客户浏览器信息
QUERY_STRING 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即使所传递的信息。这个信息经跟在CGI程序名的后面,两者中间用一个问号'?'分隔
REQUEST_METHOD 提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义

所以,关于服务端怎么向CGI程序传递请求信息的问题就可以解释了。在服务端接收到用户请求的时候需要按照CGI协议通过putenv设置环境变量,然后PHP程序也是按照CGI协议解析环境变量(PHP中使用$_SERVER即可)。

程序的实现,可以在这里下载

最后需要说明的,当服务器执行php cgi-bin/get_user.php的时候,实际上是使用php的命令行模式执行的,因为PHP已经为我们封装好了环境变量,所以我们只需要从$_SERVER中就可以获取我们需要的环境变量。这里只为了说明问题,实际上PHP的sapi里面有单独实现cgi接口。当然你也可以用shell实现,shell的话是直接获取环境变量

CGI程序的缺点

CGI工作原理:每当客户请求CGI的时候,WEB服务器就请求操作系统生成一个新的CGI解释器进程,CGI进程则处理完一个请求后退出,下一个请求来时再创建新进程。我们知道,执行一个PHP程序的必须要先解析php.ini文件,然后模块初始化等等一系列工作,每次都反复这样非常浪费资源。当然,这样在访问量很少没有并发的情况也行。可是当访问量增大,并发存在,这种方式就不适合了。这也是CGI程序一个致命的缺点。

FastCGI协议和php-cgi

FastCGI协议

既然上面提到了CGI程序的一个致命缺陷,所以在CGI协议的基础上,又设计了FASTCGI协议

,协议的内容可以查看,概括一下,主要做了以下改变

1. FastCGI被设计用来支持常驻(long-lived)应用进程,减少了fork-and-execute带来的开销
2. FastCGI进程通过监听的socket,收来自Web服务器的连接,这样FastCGI进程可以独立部署
3. 服务器和FastCGI监听的socket之间按照消息的形式发送环境变量和其他数据

所以,我们称实现了FastCGI协议的程序为FastCGI程序,在php中的sapi/cgi/php-cgi实现了这个协议,虽然它的名字取名为php-cgi,但其实是支持FacsCGI协议的。所以,实现了FastCGI协议的服务器和FastCGI程序的交互方式如下图所示

PHP中FastCGI使用

可能很少人用过php-cgi,因为目前大家都使用php-fpm(先不讨论php-fpm)。这里介绍一下php-cgi的使用。

1.php-cgi如何启动监听socket

php-cgi可执行程序为编译后的php目录下面的sapi/cgi/php-cgi。通过-b参数指定需要监听的ip和端口即可,比如我们想绑定本机的9004端口

shell> ./php-cgi -b 127.0.0.1:9004

2.如何启动多个FastCGI进程

这里需要使用PHP_FCGI_CHILDREN环境变量表示派生多少个子进程,我们可以写一个shell来启动php-cgi,点击可以下载这个shell文件。注意替换成自己的cgi路径后再使用

3.通过shell脚本启动FastCGI管理器

执行start.sh脚本之后,可以查看到启动了多个FastCGI进程

shell> ps aux  | grep php-cgi | grep -v grep
vagrant 6785 0.0 0.0 1936 524 pts/3 S 01:07 0:00 sh -c /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6792 0.0 0.3 13760 3188 ? Ss 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6793 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6794 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6795 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6796 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6797 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6798 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6799 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004
vagrant 6800 0.0 0.1 13760 1384 ? S 01:07 0:00 /home/vagrant/php-5.5.10/sapi/cgi/php-cgi -b 127.0.0.1:9004

4.和Nginx配合测试

在Nginx配置文件中加上这一段

location ~ \.php$ {
fastcgi_pass 127.0.0.1:9004;
include fastcgi.conf;
}

重启nginx之后就可以测试nginx访问php了。

php-cgi的的缺点

php-cgi实现的已经很完美了,但是我们的需求总是有点苛刻,导致它暴露了一下问题

  1. 当我们更改配置文件后,php-cgi显然无法平滑重启
  2. 我们fork的进程个数和请求量正比,请求繁忙时,fork进程多,动态调整,显然php-cgi没做到

spawn-fcgi和PHP-FPM

既然上面提及php-cgi实现的FastCGI问题官方没有解决。那自然有第三方做这个事情,我们可以使用Lighttpd的spawn-fcgi来管理php-cgi进程。注意,这里的spawn-fcgi是进程管理器,只是管理实现FastCGI协议的php-cgi,这个的安装和使用方式可以参照鸟哥的博客

还有一个做的比较好的,php-fpm,以前php-fpm并没有纳入php安装包文件中,只是在它的卓越表现,得到了越来越多的人认可,后面就被纳入了官方默认安装包文件中。php-fpm是从5.3开始才进入PHP源代码主干的,可以独立运行,不依赖php-cgi,换句话说,他自己实现了FastCGI协议。

所以,spawn-fcgi和PHP-FPM不是同一个东西,前者是进程管理器,后者却实现FastCGI协议并且支持进程平滑重启

从CGI到FastCGI到PHP-FPM的更多相关文章

  1. 网关协议学习:CGI、FastCGI、WSGI、uWSGI

    一直对这四者的概念和区别很模糊,现在就特意梳理一下它们的关系与区别. CGI CGI即通用网关接口(Common Gateway Interface),是外部应用程序(CGI程序)与Web服务器之间的 ...

  2. CGI 和 FastCGI 协议的运行原理

    目录 介绍 深入CGI协议 CGI的运行原理 CGI协议的缺陷 深入FastCGI协议 FastCGI协议运行原理 为什么是 FastCGI 而非 CGI 协议 CGI 与 FastCGI 架构 再看 ...

  3. 简单了解下CGI、FastCGI和php-fpm的概念和区别和运行原理

    什么是CGI? CGI(Common Gateway Interface),公共网关接口,它是Web服务器与外部应用程序(CGI程序)之间传递信息的接口标准.通过CGI接口,Web服务器就能够获取客户 ...

  4. 网关协议:CGI、FastCGI、WSGI

    CGI就像是一座桥,把网页和WEB服务器中的执行程序连接起来,它把HTML接收的指令传递给服务器的执行程序,再把服务器执行程序的结果返还给HTML页. CGI CGI即通用网关接口(Common Ga ...

  5. 【转】网关协议学习:CGI、FastCGI、WSGI、uWSGI

    一直对这四者的概念和区别很模糊,现在就特意梳理一下它们的关系与区别. CGI CGI即通用网关接口(Common Gateway Interface),是外部应用程序(CGI程序)与Web服务器之间的 ...

  6. 【转】CGI 和 FastCGI 协议的运行原理

    介绍 深入CGI协议 CGI的运行原理 CGI协议的缺陷 深入FastCGI协议 FastCGI协议运行原理 为什么是 FastCGI 而非 CGI 协议 CGI 与 FastCGI 架构 再看 Fa ...

  7. PHP面试经常被问cgi、fastcgi、php-fpm、mod_php的区别

    cgi.fastcgi.php-fpm.mod_php cgi cgi是公共网关接口,用户通过浏览器来访问执行再服务器上的动态程序,CGI是Web 服务器与CGI程序间传输数据的标准.准确来说是一种协 ...

  8. CGI与FastCGI nginx+PHP-FPM

    本文转载自CGI与FastCGI 1.当我们在谈到cgi的时候,我们在讨论什么 最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html. ...

  9. CGI与FastCGI

    当我们在谈到cgi的时候,我们在讨论什么 最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html.事物总是不 断发展,网站也越来越复杂, ...

  10. php CGI、Fastcgi、PHP-FPM的详细介绍与之间的关系

    以下PHP CGI.Fastcgi.PHP-FPM的一些信息归纳和汇总----->详细介绍与之间的关系 一:CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的 web ...

随机推荐

  1. Sobel导数

    Sobel 导数 目标 本文档尝试解答如下问题: 如何使用OpenCV函数 Sobel 对图像求导. 如何使用OpenCV函数 Scharr 更准确地计算  核的导数. 原理 Note 以下内容来自于 ...

  2. java:类集操作,多对多的关系

    java:类集操作,多对多的关系 //一个课程有多个学生报名, //一个学生可以报名多个课程 demo.java, Student.java, Course.java' public class Co ...

  3. 十九 Django框架,发送邮件

    全局配置settings.py EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' #发送邮件引擎 EMAIL_USE_TLS ...

  4. JavaUtil_01_MD5加密

    一.百度翻译MD5工具类 昨天做java微信开发,引用百度翻译API给公众号添加翻译功能时,需要使用MD5生成签名.注意,使用MD5生成签名后一定要转成小写,不然百度翻译后台不会认你这个签名的,会报无 ...

  5. 关于CMainFrm不接收鼠标事件响应原因

    CMainFrm即主框架窗口对鼠标的左键和右键在OnLButtonDown中无响应 解决方案: 1.在OnNcLButtonDown中响应.(即非客户区中响应) 参考文献:http://www.cod ...

  6. Mybatis学习--Sql语句构建器

    学习笔记,选自Mybatis官方中文文档:http://www.mybatis.org/mybatis-3/zh/statement-builders.html 问题 Java程序员面对的最痛苦的事情 ...

  7. FFmpeg 基本用法--此文转载,作为笔记

    v  容器(Container) v  容器就是一种文件(封装)格式,比如flv.mkv.ts.mp4.rmvb.avi等.包含下面5种流以及文件头信息. v  流(Stream) v  是一种视频数 ...

  8. XMPP群聊消息重复,自己收到自己发出的消息,群警告消息如何屏蔽

    在XMPP的"groupchat"中,创建群的时候会收到群发的"This room is locked from entry until configuration is ...

  9. 洛谷【P1104】生日(插入排序版)

    题目传送门:https://www.luogu.org/problemnew/show/P1104 题目很简单,我主要是来讲插入排序的. 所谓插入排序,就是从待排序数组不断将数据插入答案数组里. 假设 ...

  10. poj 3469 Dual Core CPU——最小割

    题目:http://poj.org/problem?id=3469 最小割裸题. 那个限制就是在 i.j 之间连双向边. 根据本题能引出网络流中二元关系的种种. 别忘了写 if ( x==n+1 ) ...