Web服务器(也称WWW服务器,一般指网站服务器)主要功能是提供网上信息浏览服务,目前最主流的三个Web服务器是Apache、Nginx、IIS。
可以简单理解为,如果在一台主机上配置了服务器软件,使它可以解析HTTP协议并处理HTTP请求,那这台主机就是一台Web服务器了。
在之前一篇文章Nginx反向代理服务器的配置(详细)里我们介绍过,Nginx可以通过简单配置就能搭建出一个Web服务器,但是Nginx不能直接处理C++程序。
也就是说这样配置的Nginx服务器只能处理静态请求,不能直接处理动态请求。可能有的小伙伴会说了,什么静态动态请求,我不知道你在说什么?
静态请求是指:客户端通过网络(Network)连接到服务器上,使用HTTP协议发起一个请求(Request),告诉服务器我现在需要得到哪个页面,所有的请求交给Web服务器之后,服务器根据用户的需要,从文件系统(存放了所有静态页面的磁盘)取出内容。之后通过Web服务器返回给客户端,客户端接收到内容之后得到显示的效果。
静态请求不需要访问数据库,页面是固定的,无论谁访问都不变,静态页面资源采用HTML开发。
动态请求是指:客户端请求的是动态资源,比如登录这个请求,总不能每个人登录得到的页面都是一样的吧?所以类似登录请求就是个动态请求了,动态请求一般会携带一些数据,Web服务器需要根据请求的数据去拼凑出一个动态页面返回给客户端。
Nginx不能处理这些动态请求的数据,怎么办呢?交给其他角色来处理——CGI程序就是用来干这个的。
通用网关接口(Common Gateway Interface、CGI)描述了客户端和服务器程序之间传输数据的一种标准,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。
这是一种官方的定义,简单理解就是,CGI程序是运行在Web服务器上的一段程序,可以用来处理动态请求。
CGI独立于任何语言的,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unix shell script、Python、 Ruby、PHP、 perl、Tcl、 C/C++和 Visual Basic 都可以用来编写 CGI 程序。
工作流程是:
- web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输入传递数据;
- CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等;
- CGI进程将处理结果通过标准输出、标准错误,传递给web服务器;
- web服务器收到CGI返回的结果,构建Http Response返回给客户端,并杀死CGI进程。
CGI使外部程序与Web服务器之间交互成为可能。CGI程序运行在独立的进程中,并对每个Web请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。
快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。
FastCGI工作流程是:
- Web服务器启动时载入初始化FastCGI执行环境。
- FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web服务器的连接。
- 当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。
- FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。
- FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。
简单来说由于FastCGI程序并不需要不断的产生新进程,它可以维持CGI程序的生存期,大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI 技术提高 5 倍以上。
让人开心的是,Nginx支持FastCGI代理,接收客户端的请求,然后将请求转发给后端FastCGI进程。
由于FastCGI进程由FastCGI进程管理器管理,而不是Nginx。所以我们还需要一个FastCGI进程管理器,管理我们编写FastCGI程序。
spawn-fcgi是一个通用的FastCGI进程管理器,简单小巧,它的功能主要是打开监听端口,绑定地址,然后创建我们编写的FastCGI应用程序进程,退出完成工作。FastCGI应用程序初始化,然后进入死循环侦听socket的连接请求。
好了好了,我们整理一下思绪,为了让Nginx服务器能够处理动态请求,我们需要为Nginx服务器配置FastCGI,同时使用spawn-fcgi作为FastCGI的进程管理器。
每当有动态请求数据到达服务器的时候,Nginx就把数据放到某个固定的端口,而spawn-fcgi就会把数据转发给FastCGI处理,处理好的结果再返回给Nginx。
这样一切不就好起来了嘛!
接下来,我们开始在Ubuntu上捣鼓起来
- spawn-fcgi源码安装
#如果没有安装automake工具,ubuntu用下面的命令安装
sudo apt-get install autoconf automake libtool
#spawn-fcgi源码安装包链接,因为网络原因我没去试
sudo wget http://download.lighttpd.net/spawn-fcgi/releases-1.6.x/spawn-fcgi-1.6.4.tar.gz
tar -zxvf spawn-fcgi-1.6.4.tar.gz
cd spawn-fcgi-1.6.4/
./configure
make
sudo make install
- FastCGI源码安装
sudo wget http://www.filewatcher.com/d/Gentoo/distfiles/Other/fcgi-2.4.1-SNAP- 0910052249.tar.gz.614929.html
tar -zxvf fcgi-2.4.1-SNAP-0910052249.tar.gz
cd fcgi-2.4.1-SNAP-0910052249/
./configure
make
sudo make install
最后可以做个简单测试
- fastcgi_test.c 源程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fcgi_stdio.h"
int main(int argc, char *argv[])
{
int count = 0;
//阻塞等待并监听某个端口,等待Nginx将数据发过来
while (FCGI_Accept() >= 0)
{
//如果想得到数据,需要从stdin去读,实际上从Nginx上去读
//如果想上传数据,需要往stdout写,实际上是给Nginx写数据
printf("Content-type: text/html\r\n");
printf("\r\n");
printf("<title>Fast CGI Hello!</title>");
printf("<h1>Fast CGI Hello!</h1>");
//SERVER_NAME:得到server的host名称
printf("Request number %d running on host <i>%s</i>\n", ++count, getenv("SERVER_NAME"));
}
return 0;
}
- 编译代码
gcc fcgi_test.c -o test -lfcgi
# 需要添加静态库 -lfcgi
- cgi程序与spawn-fcgi绑定
spawn-fcgi -a 127.0.0.1 -p 8001 -f ./test
# 调用格式:spawn-fcgi -a IP -p 端口 -f fastcgi程序
# -a IP: 服务器IP地址
# -p port: 服务器将数据发送到的端口
# -f cgi程序: spawn-fcgi启动的可执行fastcgi程序
- Nginx配置
#监听用户的test请求,通过fastcgi_pass交给本地8001端口处理
#此时spwan-cgi已经将8001端口交给之前我们写好的test进程处理
location /test {
fastcgi_pass 127.0.0.1:8001;
# fastcgi_pass也可以是localhost或者本机真实IP
fastcgi_index test;
include fastcgi.conf;
}
当在浏览器输入http://localhost/test,Nginx收到http://localhost/test请求时,会匹配到 location /test 模块,然后将请求传到后端的FastCGI应用程序处理: