Nginx实现FastCGI

发布于:2025-08-12 ⋅ 阅读:(18) ⋅ 点赞:(0)

什么是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

PHP: Hypertext Preprocessor

安装环境

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 identity

This 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


网站公告

今日签到

点亮在社区的每一天
去签到