1. CGI是什么

CGI是common gateway interface的缩写,大家都译作通用网关接口,但很不幸,我们无法见名知意。

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

简单版的cgi工作方式如下:

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

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再返回给浏览器。

有多种方式可以执行cgi程序,但对http的请求方法来说,只有get和post两种方法允许执行cgi脚本(即上面的search程序)。实际上post方法的内部本质还是get方法,只不过在发送http请求时,get和post方法对url中的参数处理方式不一样而已。

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

2.各种术语释疑

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

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

  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

简单说明CGI和动态请求是什么的更多相关文章

  1. IIS 7 反向代理 URL重写 转发动态请求

    一.反向代理是什么 有一篇文章说的挺好的 Nginx 反向代理.负载均衡.页面缓存.URL重写及读写分离详解 http://www.server110.com/nginx/201402/5534.ht ...

  2. 简单说明CGI是什么

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

  3. 写了一个简单的CGI Server

    之前看过一些开源程序的源码,也略微知道些Apache的CGI处理程序架构,于是用了一周时间,用C写了一个简单的CGI Server,代码算上头文件,一共1200行左右,难度中等偏上,小伙伴可以仔细看看 ...

  4. jsonp其实很简单【ajax跨域请求】

    js便签笔记(13)——jsonp其实很简单[ajax跨域请求] 前两天被问到ajax跨域如何解决,还真被问住了,光知道有个什么jsonp,迷迷糊糊的没有说上来.抱着有问题必须解决的态度,我看了许多资 ...

  5. 基于RxJava2+Retrofit2简单易用的网络请求实现

    代码地址如下:http://www.demodashi.com/demo/13473.html 简介 基于RxJava2+Retrofit2实现简单易用的网络请求,结合android平台特性的网络封装 ...

  6. Loadrunner与kylinPET的能力对比测试--web动态请求

    概述 在<性能测试工具选择策略--仿真度对比测评分析报告>一文详细分析了使用相同的web页面,分别使用LoadRunner,Jmeter,kylinTOP工具进行录制脚本并执行得出在静态请 ...

  7. python web.py实现简单的get和post请求

    使用web.py框架,实现简单的get和post请求: py文件名:mytest.py import web urls = ( '/', 'hello' ) app = web.application ...

  8. 搭建简单的CGI应用程序

    原文来源于<核心编程3>第10章web编程 一.静态文件+脚本文件 1.首先开启cgiweb服务器 python2 -m CGIHTTPServer 8000 看到如下反应 2.服务器目录 ...

  9. python之简单的get和post请求

    1.json 模块提供了一种很简单的方式来编码和解码JSON数据. 其中两个主要的函数是 json.dumps() 和 json.loads() , 要比其他序列化函数库如pickle的接口少得多. ...

随机推荐

  1. tp5自定义分页参数

    代码示例: $data = db('activity') -> where($condition1)-> order('startline desc') -> paginate(2, ...

  2. Particle Filters

    |—粒子滤波原理 |—基础代码的建立—|—前进 |                               |—转弯 |                               |—噪音(误差 ...

  3. java0618

    1. java的基本数据类型,各占多少字节? byte 8位 short 16位 int 32位 long 64位 float 32位 double 64位 boolean 1位 char 16位 2 ...

  4. shell 命令 grep -R 查询包含指定内容的文件

    grep -R 举个栗子,在有上百个sql文件的目录下,查找使用 spark引擎 执行的文件. 代码是: grep -R spark ./* 返回的就是包含  spark 的sql文件名.

  5. JavaScript视频分享,学无止境。

    http://blog.sina.com.cn/s/blog_735ed39a0102voy3.html

  6. iOS 数组问题

    在iOS开发过程中,使用json抓取网络数据进行解析时,用tableview承载,发现数据已经抓取到,但是在cell里面使用却会导致程序崩溃 原因可能是初始化方法问题,将 _infoArray=[[N ...

  7. 【百度杯】ctf夺旗大战,16万元奖励池等你拿

    寻找安全圈内最会夺flag的CTF职业玩家,将以个人方式参与夺旗,完全凭借个人在CTF中的技艺及造诣获得奖金回报. 周末少打一局LOL,玩一玩CTF也能挣个万元零花钱! **比赛时间: 9月至17年3 ...

  8. Javascript高级编程学习笔记(49)—— DOM2和DOM3(1)DOM变化

    DOM变化 我们知道DOM有许多的版本,其中DOM0和DOM2这两个级别以对事件的纳入标准而为人所知 但是呢,这里不讲事件,在后面会有专门和事件有关的部分作为详细讲解 这里就只讲一下DOM2和DOM3 ...

  9. LSTM和GRU

    LSTM和GRU LSTM 忽略偏置: \[\begin{align} i_t&=\sigma(x_t\cdot W_i+h_{t-1}\cdot U_i)\\ f_t&=\sigma ...

  10. CentOS7编译安装MySQL5.7.24

    目录 安装依赖 安装boost 编译安装MySQL 配置 登录MySQL,修改密码 安装依赖 (1)cmake是新版MySQL的编译工具 sudo yum install gcc gcc-c++ pc ...