什么是CGI?
早期的Web服务器只能简单的响应浏览器发送的HTTP请求,将存储在服务器上的HTML文件给返回给浏览器,也是静态html文件,但是后期随着网址功能增多网站开发也越来越复杂,以至于后来出现动态技术比如php、java、python等等,但是nginx/apache服务器并不能直接运行php这类文件,apache实现的方式是通过打补丁的方式。但是nginx通过与第三方基于协议实现,即通过某种特定协议将客户端请求转发给第三方服务处理,第三方服务器会新建新的进程 处理用户的请求,,处理完成后返回数据给nginx然后回收进程,最后nginx再返回给客户端,其中那个约定就是通过网络接口(common gateway interface),CGI协议是web服务器和外部应用程序之间的接口标准,是cgi程序和web服务器之间传递信息的标准化接口
FastCGI?
CGI协议虽然解决了语言解析器和 Web Server 之间通讯的问题,但是它的效率很低,因为 Web Server 每收到一个请求都会创建一个CGI进程,PHP解析器都会解析php.ini文件,初始化环境,请求结束的时候 再关闭进程,对于每一个创建的CGI进程都会执行这些操作,所以效率很低,而FastCGI是用来提高CGI性 能的,FastCGI每次处理完请求之后不会关闭掉进程,而是保留这个进程,使这个进程可以处理多个请 求。这样的话每个请求都不用再重新创建一个进程了,大大提升了处理效率。
本篇文章用到PHP-FPM作为示例,展示怎么运用CGI。
FPM(FastCGI Process Manager)是一个实现了Fast CGI的程序,并且提供管理的功能。进程包括master和worker进程,master负责监听端口,接受来自web server的请求,worker进程会有多个,每一个进程都会嵌入一个PHP解析器,进行PHP代码的处理
安装nginx
这里使用源码安装,版本:1.26 nginx官网:nginx: download
#安装环境所需要的
dnf install gcc pcre-devel zlib-devel openssl-devel -y
#创建一个不能登录的用户,不创建家目录
useradd -s /sbin/nologin -M nginx
#解压 编译
tar -zxvf nginx-1.26.1.tar.gz -C /usr/local/
cd /usr/local/nginx-1.26.1/
./configure --prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \ #支持https
--with-http_v2_module \ #支持httpv2版本
--with-http_realip_module \ #支持IP透传
--with-http_stub_status_module \ #支持状态也变
--with-http_gzip_static_module \ #支持压缩
--with-pcre \ #支持正则表达
--with-stream \ #支持tcp反向代理
--with-stream_ssl_module \ #支持ssl加密
--with-stream_realip_module #支持反向代理的IP透出
make && make install
编辑环境变量
[root@localhost sbin]# cat /etc/profile.d/nginx.sh
#!/bin/bash
export PATH=$PATH:/usr/local/nginx/sbin
#加载一下
[root@localhost sbin]# source /etc/profile.d/nginx.sh
[root@localhost sbin]# nginx -v
nginx version: nginx/1.26.1
安装 PHP
安装环境
yum install -y bzip2 systemd-devel libxml2-devel sqlite-devel libpng-devel libcurl-devel oniguruma-devel
编译,这里的版本是8.3.9的
gzip -d php-8.3.9.gz
tar -xvf php-8.3.9
#编译
cd php-8.3.9/
[root@localhost php-8.3.9]# ./configure \
> --prefix=/usr/local/php \
> --with-config-file-path=/usr/local/php/etc \ #指定配置路径
> --enable-fpm \ #用CGI方式启动程序----!
> --with-fpm-user=nginx \
> --with-fpm-group=nginx \
> --with-curl \ #支持curl
> --with-iconv \ #启用iconv函数,转换字符编码
> --with-mhash \ #mhash 加密方式
> --with-zlib \ #支持zlib,用于http传输的
> --with-openssl \ #支持ssl加密
> --enable-mysqlnd \ #mysql数据库
> --with-mysqli \
> --with-pdo-mysql \
> --disable-debug \ #关闭debug功能
> --enable-sockets \ #支持套接字访问
> --enable-soap \ #支持soap扩展协议
> --enable-xml \ #支持xml
> --enable-ftp \ #支持ftp
> --enable-gd \ #支持gd库
> --enable-exif \ #支持图片元数据
> --enable-mbstring \ #支持多字节字符串
> --enable-bcmath \ #打开图片大小调整,用到zabbix监控的时候用到了这个模块
> --with-fpm-systemd #支持systemctl 管理cgi
如果在编译的时候发现有关于oniguruma依赖:
Package 'oniguruma', required by 'virtual:world', not found
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.Alternatively, you may set the environment variables ONIG_CFLAGS
and ONIG_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
通过网盘分享的文件:php
链接: https://pan.baidu.com/s/1ftANK6j3vBvefDH-yPoMfg 提取码: 2333
[root@localhost ~]# rpm -ivh oniguruma-6.9.6-1.el9.6.x86_64.rpm --force
warning: oniguruma-6.9.6-1.el9.6.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID 350d275d: NOKEY
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...
1:oniguruma-6.9.6-1.el9.6 ################################# [100%]
[root@localhost ~]# rpm -ivh oniguruma-devel-6.9.6-1.el9.6.x86_64.rpm --force
warning: oniguruma-devel-6.9.6-1.el9.6.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID 350d275d: NOKEY
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...
1:oniguruma-devel-6.9.6-1.el9.6 ################################# [100%]
即可重新编译一下,然后执行make && make install
php优化配置
[root@localhost php-8.3.9]# cd /usr/local/php/etc
[root@localhost etc]# cp php-fpm.conf.default php-fpm.conf
[root@localhost etc]# vim php-fpm.conf
...
pid = run/php-fpm.pid #去掉注释
...
[root@localhost etc]# cd php-fpm.d/
[root@localhost php-fpm.d]# ls
www.conf.default
[root@localhost php-fpm.d]# cp www.conf.default www.conf
[root@localhost ~]# cp php-8.3.9/php.ini-production /usr/local/php/etc/php.ini
[root@localhost ~]# cd /usr/local/php/etc/
[root@localhost etc]# vim php.ini
...
[Date]
; Defines the default timezone used by the date functions
; https://php.net/date.timezone
date.timezone = Asia/Shanghai #添加一个时区
[root@localhost etc]# cd /root/php-8.3.9/
[root@localhost php-8.3.9]# cp sapi/fpm/php-fpm.service /lib/systemd/system/ #配置系统服务
[root@localhost php-8.3.9]# vim /lib/systemd/system/php-fpm.service
...
# Mounts the /usr, /boot, and /etc directories read-only for processes invoked by this unit.
#ProtectSystem=full #注释掉这一行
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start php-fpm.service
[root@localhost ~]# netstat -lntup | grep php
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 185519/php-fpm: mas
为其添加环境变量
[root@localhost nginx]# cat /etc/profile.d/php-cgi.sh
#!/bin/bash
PATH=$PATH:/usr/local/php/bin
[root@localhost nginx]# source /etc/profile.d/php-cgi.sh
测试
准备一个测试页面
mkdir /data/php -p
[root@localhost ~]# cat /data/php/index.php
<?php
phpinfo();
?>
然后写一个nginx的虚拟主机:先在/usr/local/nginx/conf/nginx.conf 文件里面添加一行i
vim /usr/local/nginx/conf/nginx.conf
...
include conf.d/*.conf; #这个在http{}里面,和server{}同级
...
[root@localhost ~]# cat /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name php.openlab.com;
root /data/php;
location ~\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf
}
}
nginx -s reload && systemctl disable --now firewalld && setenforce 0
然后在本机的hosts上面加上解析: IP php.openlab.com
优化1.0
Fast CGI还是会对访问的php请求进行单开一个worker进行处理,即使处理的请求一样。我们可以利用memcached将同一请求的结果进行一定时间的缓存,如果一定时间内则将缓存的结果给到memcached,它来提供响应就能大大提高效率
安装memcached插件
安装memcached插件,安装见php官网提供的memcached:PECL :: Package :: memcache 8.2
我这里使用8.2版本的
[root@localhost ~]# ls
anaconda-ks.cfg Music Pictures
Desktop nginx-1.26.1.tar.gz Public
Documents oniguruma-6.9.6-1.el9.6.x86_64.rpm Templates
Downloads oniguruma-devel-6.9.6-1.el9.6.x86_64.rpm Videos
memcache-8.2 package.xml
memcache-8.2.tgz php-8.3.9
[root@localhost ~]# tar -zxvf memcache-8.2.tgz
[root@localhost ~]# cd memcache-8.2/
[root@localhost memcache-8.2]# ls
config9.m4 config.w32 docker example.php memcache.php src
config.m4 CREDITS Dockerfile LICENSE README tests
[root@localhost memcache-8.2]# dnf install autoconf
[root@localhost memcache-8.2]# phpize
Configuring for:
PHP Api Version: 20230831
Zend Module Api No: 20230831
Zend Extension Api No: 420230831
[root@localhost memcache-8.2]# ./configure && make && make install
phpize的作用是为当前系统正在使用的 PHP 版本“就地”生成一份专用的扩展编译环境,相当于给这份扩展源码打补丁,让它能针对当前这台机器的 PHP 进行编译。
随后就可以用 ./configure && make && make install
把 memcached 扩展编译并安装到 PHP 的扩展目录里。
然后可以将测试文件复制到我们的nginx发布目录中
[root@localhost memcache-8.2]# cp example.php memcache.php /data/php/
编辑memcached.php这个文件
$VERSION='$Id$';
define('ADMIN_USERNAME','openlab'); // Admin Username
define('ADMIN_PASSWORD','OPENlab123'); // Admin Password
define('DATE_FORMAT','Y/m/d H:i:s');
define('GRAPH_SIZE',200);
define('MAX_ITEM_DUMP',50);
#这里改为本地即可
$MEMCACHE_SERVERS[] = 'localhost:11211'; // add more as an array
然后php加载memcached模块:然后加载一下php-fpm, systemctl reload php-fpm
[root@localhost ~]# vim /usr/local/php/etc/php.ini
...
extension=memcache
...
部署memcached
memcachd可以部署在不同的主机上面,上面memcached.php其中就是指向memcached后端的一个监控,根据自己想要部署在哪一台主机决定。这里就部署在本机上面
[root@localhost ~]# dnf install memcached -y
[root@localhost ~]# systemctl reload php-fpm.service
[root@localhost ~]# systemctl enable --now memcached.service
Created symlink /etc/systemd/system/multi-user.target.wants/memcached.service → /usr/lib/systemd/system/memcached.service.
[root@localhost ~]# netstat -lntup | grep memcached
tcp 0 0 127.0.0.1:11211 0.0.0.0:* LISTEN 192386/memcached
tcp6 0 0 ::1:11211 :::* LISTEN 192386/memcached
它的配置文件
[root@localhost ~]# cat /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS="-l 127.0.0.1,::1"
优化1.0测试
登录memcace.php,然后不断地访问example.php测试命中率
我们新开一台主机,用于ab测试,在里面写好/etc/hosts文件
192.168.118.161 php.openlab.com
[root@localhost ~]# dnf whatprovides ab
Updating Subscription Management repositories.
Unable to read consumer identityThis system is not registered with an entitlement server. You can use subscription-manager to register.
Last metadata expiration check: 20:30:15 ago on Tue 05 Aug 2025 04:08:18 PM CST.
httpd-tools-2.4.57-5.el9.x86_64 : Tools for use with the Apache HTTP Server
Repo : appstream
Matched from:
Filename : /usr/bin/ab[root@localhost ~]# dnf install httpd-tools-2.4.57-5.el9.x86_64 -y
[root@localhost ~]# ab -n500 -c10 http://php.openlab.com/index.php
...
Concurrency Level: 10
Time taken for tests: 0.243 seconds
Complete requests: 500
Failed requests: 65 #失败的次数
(Connect: 0, Receive: 0, Length: 65, Exceptions: 0)
Total transferred: 36331932 bytes
HTML transferred: 36250932 bytes
Requests per second: 2058.88 [#/sec] (mean)
Time per request: 4.857 [ms] (mean)
Time per request: 0.486 [ms] (mean, across all concurrent requests)
Transfer rate: 146100.07 [Kbytes/sec] received
[root@localhost ~]# ab -n500 -c10 http://php.openlab.com/example.php
....
Concurrency Level: 10
Time taken for tests: 0.079 seconds
Complete requests: 500
Failed requests: 0 #没有失误的请求
Non-2xx responses: 500
Total transferred: 90500 bytes
HTML transferred: 0 bytes
Requests per second: 6329.11 [#/sec] (mean)
Time per request: 1.580 [ms] (mean)
Time per request: 0.158 [ms] (mean, across all concurrent requests)
Transfer rate: 1118.72 [Kbytes/sec] received
可以看到有明显的差距,后者是没有任何丢包且速度更快。
原因是因为examples.php的代码中使用了memcached,里面指向了本地的memcache,然后通过php将处理给到了memcache
<?php
$memcache = memcache_connect('localhost', 11211);
if ($memcache) {
$memcache->set("str_key", "String to store in memcached");
$memcache->set("num_key", 123);
$object = new StdClass;
$object->attribute = 'test';
$memcache->set("obj_key", $object);
$array = Array('assoc'=>123, 345, 567);
$memcache->set("arr_key", $array);
var_dump($memcache->get('str_key'));
var_dump($memcache->get('num_key'));
var_dump($memcache->get('obj_key'));
}
else {
echo "Connection to memcached failed";
}
?>
优化2.0
上面还是不够快,因为同一请求还需要走php,明明nginx就可以直接到mmcache,下面就是php的高速缓存配置
由于我们编译nginx的时候没有开启memc和srccache的功能,所以我们需要卸载nginx重新装
[root@localhost ~]# nginx -s stop
[root@localhost ~]# netstat -lntup | grep nginx
[root@localhost ~]# rm -rf /etc/profile.d/nginx.sh
[root@localhost ~]# rm -rf /usr/local/nginx*
[root@localhost ~]# ls /usr/local/
bin etc games include lib lib64 libexec php sbin share src
[root@localhost ~]# find / -name nginx*
/root/nginx-1.26.1.tar.gz
使用到的模块:(我给的网盘链接里面也有)
OpenResty - Memc Nginx 模块 - OpenResty 中文
OpenResty - Srcache Nginx Module
[root@localhost ~]# tar zxf srcache-nginx-module-0.33.tar.gz
[root@localhost ~]# tar zxf memc-nginx-module-0.20.tar.gz
[root@localhost ~]# tar zxf nginx-1.26.1.tar.gz
编译与安装
./configure --prefix=/apps/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-stream \
--with-stream_ssl_module \
--with-stream_realip_module \
--add-module=/root/memc-nginx-module-0.20 \ #指定我们解压的模块
--add-module=/root/srcache-nginx-module-0.33 #指定模块路径
make && make install
同理在主配置文件/apps/nginx/conf/nginx.conf中加上一行include conf.d/*.conf
vim /apps/nginx/conf/conf.d/vhost.conf
#配置环境变量
[root@localhost nginx-1.26.1]# cat /etc/profile.d/nginx.sh
#!/bin/bash
export PATH=$PATH:/apps/nginx/sbin
[root@localhost nginx-1.26.1]# source /etc/profile.d/nginx.sh
配置内容:
[root@localhost ~]# cat /apps/nginx/conf/conf.d/vhost.conf
upstream memcache {
server 127.0.0.1:11211;
keepalive 512;
}
server {
listen 80;
server_name php.openlab.com;
root /data/php;
location /memc {
internal; #只允许内部访问
memc_connect_timeout 100ms;
memc_send_timeout 100ms;
memc_read_timeout 100ms;
set $memc_key $query_string; # 以 query_string 为缓存键
set $memc_exptime 300; # 缓存 300 秒
memc_pass memcache;
}
location ~\.php$ {
set $key $uri$args; # 缓存键 = 完整 URI + 查询串
srcache_fetch GET /memc $key;# 先尝试从 /memc 读取缓存
srcache_store PUT /memc $key;# 响应结束后把结果写回 /memc
fastcgi_pass 127.0.0.1:9000; # 如果缓存未命中,把请求转给 PHP-FPM
fastcgi_index index.php;
include fastcgi.conf;
}
}
srcache_fetch GET表示将向内部发起一次路径为/memc的请求,将key携带(GET /memc?{$key})同时不再往下面执行,如果没有的话就走fastcgi,然后将处理之后内容通过srcache_store PUT同样的方式发送请求给memcache存储
然后重启一下nginx -s reload
在另一台主机上面进ab测试
[root@localhost ~]# ab -n500 -c10 http://php.openlab.com/example.php
...
Concurrency Level: 10
Time taken for tests: 0.084 seconds
Complete requests: 500
Failed requests: 0
Non-2xx responses: 500
Total transferred: 90500 bytes
HTML transferred: 0 bytes
Requests per second: 5737.89 [#/sec] (mean)
Time per request: 1.743 [ms] (mean)
Time per request: 0.174 [ms] (mean, across all concurrent requests)
Transfer rate: 1014.22 [Kbytes/sec] received