简单说明CGI和动态请求是什么

1. CGI是什么

CGI是common gateway interface的缩写,你们都译做通用网关接口,但很不幸,咱们没法见名知意。php

咱们知道,web服务器所处理的内容都是静态的,要想处理动态内容,须要依赖于web应用程序,如php、jsp、python、perl等。可是web server如何将动态的请求传递给这些应用程序?它所依赖的就是cgi协议。没错,是协议,也就是web server和web应用程序交流时的规范。换句话说,经过cgi协议,再结合已搭建好的web应用程序,就可让web server也能"处理"动态请求(或者说,当用户访问某个特定资源时,能够触发执行某个web应用程序来实现特定功能),你确定知道处理两字为何要加上双引号。html

简单版的cgi工做方式以下:java

例如,在谷歌搜索栏中搜索一个关键词"http",对应的URL为:python

https://www.google.com/search?q=http&oq=http&aqs=chrome..69i57j69i60l4j0.1136j0j8&sourceid=chrome&ie=UTF-8

当谷歌的web server收到该请求后,先分析该url,从中知道了要执行search程序,而且还知道了一系列要传递给search的参数及其对应的value。web server会将这些程序参数和其它一些环境变量根据cgi协议经过TCP或套接字等方式传递给已启动的cgi程序(多是cgi进程,或者是已加载的模块cgi模块)。当cgi进程接收到web server的请求后,调用search程序并执行,同时还会传递参数给search程序。search执行结束后,cgi进程/线程将处理结果返回给web server,web server再返回给浏览器。nginx

有多种方式能够执行cgi程序,但对http的请求方法来讲,只有get和post两种方法容许执行cgi脚本(即上面的search程序)。实际上post方法的内部本质仍是get方法,只不过在发送http请求时,get和post方法对url中的参数处理方式不同而已。web

任何一种语言都能编写CGI,只不过有些语言比较擅长,有些语言则很是繁琐,例如用bash shell开发,那么须要用echo等打印语句将执行结果放在巨多无比的html的标签中输出给客户端。经常使用于编写CGI的语言有perl、php、python等,java也同样能写,但java的servlet彻底能实现CGI的功能,且更优化、更利于开发。chrome

2.各类术语释疑

说实话,对于一个没接触过编程语言的人来讲,刚接触cgi概念的时候确定会有一堆疑问,这究竟是什么鬼,处理动态内容的东西不是像php同样的应用程序吗,跟cgi有几毛钱关系,fastcgi又是什么?我想,非科班出身的强迫症患者(包括我)必定会被这些概念折腾的死去活来。shell

以php为例,我将一次动态请求相关的概念大体都简单解释一遍。apache

  1. cgi:它是一种协议。经过cgi协议,web server能够将动态请求和相关参数发送给专门处理动态内容的应用程序。
  2. fastcgi:也是一种协议,只不过是cgi的优化版。cgi的性能较烂,fastcgi则在其基础上进行了改进。
  3. php-cgi:fastcgi是一种协议,而php-cgi实现了这种协议。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁。
  4. php-fmp:是对php-cgi的改进版,它直接管理多个php-cgi进程/线程。也就是说,php-fpm是php-cgi的进程管理器所以它也算是fastcgi协议的实现。在必定程度上讲,php-fpm与php的关系,和tomcat对java的关系是相似的。
  5. cgi进程/线程:在php上,就是php-cgi进程/线程。专门用于接收web server的动态请求,调用并初始化zend虚拟机
  6. cgi脚本:被执行的php源代码文件。
  7. zend虚拟机:对php文件作词法分析、语法分析、编译成opcode,并执行。最后关闭zend虚拟机。
  8. cgi进程/线程和zend虚拟机的关系:cgi进程调用并初始化zend虚拟机的各类环境。

以php-fpm为例,web server从转发动态请求到结束的过程大体以下:编程

而每一个php-cgi进程的做用大体包括:(有些功能分类错误,请无视,知道大体功能就够了)

注意,尽管php-fpm的全称为PHP FastCGI Process Manager,但严格地讲,php-fpm不是fastcgi的进程管理器,而是php fastcgi即php-cgi的进程管理器。fastcgi只是一种协议,不是进程。就像http协议同样,apache对它的实现是httpd,nginx对它的实现就叫nginx。

再次说明,cgi和fastcgi是一种协议。各类支持和WEB交互的编程语言对cgi/fastcgi协议都作了各自的实现(固然,任何一种语言都能写cgi脚本),而php上的php-cgi和php-fpm正是php对fastcgi协议的实现。

3. web server和CGI的交互模式

web server对cgi进程/线程来讲,它的做用就是发起动态处理请求,传递一些参数和环境变量,最后接收cgi的返回结果。再通俗而不严谨地说,web server经过cgi/fastcgi协议将动态请求转发给执行cgi脚本的应用程序。经过下面httpd.conf中的转发配置应该很容易理解(httpd和php-fpm的交互):

ProxyRequests off
ProxyPassMatch ^/(.*\.php)$ fcgi://127.0.0.1:9000/usr/local/apache/htdocs/$1

以最典型的apache httpd和php为例,对于httpd来讲,web server和php-cgi有3种交互模式。

  • cgi模式:httpd接收到一个动态请求就fork一个cgi进程,cgi进程返回结果给httpd进程后自我销毁。
  • 动态模块模式:将php-cgi的模块(例如php5_module)编译进httpd。在httpd启动时会加载模块,加载时也将对应的模块激活,php-cgi也就启动了。(注:纠正一个小小错误,不少人觉得动态编译的模块是能够在须要的时候随时加载调用,不须要的时候它们就中止了,实际上不是这样的。和静态编译的模块同样,动态加载的模块在被加载时就被加入到激活链表中,不管是否使用它,它都已经运行在apache httpd的内部。可参考LoadModule指令的官方手册)
  • php-fpm模式:使用php-fpm管理php-cgi,此时httpd再也不控制php-cgi进程的启动。能够将php-fpm独立运行在非web服务器上,实现所谓的动静分离。

实际上,借助模块mod_fastcgi还能够实现fastcgi模式。同cgi同样,管理模式的先天缺陷决定了这并非一种好方法。

3.1 CGI模式

使用CGI模式时,当动态请求到达,httpd临时启动一个cgi解释器,并经过cgi协议转发要运行的内容。当cgi脚本运行结束后,将结果返回给httpd,而后cgi解释器进程自我销毁。当多个动态请求到达时,将前后启动多个cgi解释器。所以,这种方法效率极低。

在注释掉php5_module的LoadModule相关行后,使用action指令指定要使用cgi运行的类型。但注意,action指令是mod_action提供的,因此必须已经加载该模块。

例如:指定MIME类型为image/gif的请求使用images.cgi运行。显然,images.cgi脚本你必须先写好。

Action image/gif /cgi-bin/images.cgi

还能够经过添加handler来复合文件类型,再使用某个cgi脚本去运行这个handler中的任意类型。

AddHandler my-file-type .xyz
Action my-file-type "/cgi-bin/program.cgi"

对于php来讲,则可使用安装php时bin目录下提供的php-cgi程序做为cgi程序。

[root@xuexi php]# ls /usr/local/php/bin/
pear  peardev  pecl  phar  phar.phar  php  php-cgi  php-config  phpize

# 复制到apache默认的cgi-bin目录下,方便管理
[root@xuexi php]# cp /usr/local/php/bin/php-cgi /usr/local/apache/cgi-bin/

# 在httpd.conf中添加如下行
Action application/x-httpd-php /usr/local/php/bin/cgi-bin/php-cgi

3.2 模块方式

在编译php时,将php5_module模块编译到apache中,例如在编译php时在./configure配置中加上"--with-apxs2=/usr/local/apache/bin/apxs"。

这种交互模式下,httpd在启动时加载并激活php_module。也就是说,php-cgi常驻在httpd进程内部。当动态请求到达时,httpd不用再生成cgi解释器,而是直接将动态请求转发给它内部php-cgi。

配置实用这种交互模式很是简单,只需使用LoadModule加载php_module,再添加对应的MIME处理器便可。

LoadModule php5_module modules/libphp5.so

# 在mime模块中添加对应的类型
<IfModule mime_module>
AddType application/x-httpd-php .php
AddType applicaiton/x-httpd-php-source .phps
</IfModule>

3.3 php-fpm方式

前面说了,php-fpm是php-cgi的进程管理器。这种交互方式其实是让php-cgi以独立于httpd的方式存在,目前基本使用php-fpm的方式管理php-cgi进程。也就是说,这种模式下,php-cgi和httpd已经分离了,它们的分离意味着请求的动静分离变为可能:httpd和php-fpm分别运行在不一样服务器上。动静分离后,压力也分散到各自的服务器上。

要让php-fpm以这种方式运行,须要在编译的./configure配置选项中添加"--enable-fpm"选项。固然,还得启动php-fpm服务。例如:

service php-fpm start

这样php-cgi进程就开放着端口(默认9000)等待httpd转发动态请求。要让httpd可以转发请求到php-cgi上,须要在httpd.conf中关闭正向代理,并设置fastcgi协议代理参数。例如,转发到192.168.100.54主机上的php-fpm。

# 加载代理模块
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

# 添加MIME类型
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps

# 在须要转发的虚拟主机中配置转发代理
ProxyRequests off
ProxyPassMatch ^/(.*\.php)$ fcgi://192.168.100.54:9000/usr/local/apache/htdocs/$1
相关文章
相关标签/搜索