Nginx 与 PHP 交互?

NGINX

Nginx是俄国人最早开发的Webserver,现在已经风靡全球,相信大家并不陌生。PHP也通过二十多年的发展来到了7系列版本,更加关注性能。这对搭档在最近这些年,叱咤风云,基本上LNMP成了当下的标配。可是,你用了这么多年的Nginx+PHP的搭配,你真正知道他们之间是怎么交互怎么通信的么?作为一道常常用来面试的考题,从过往经验看,情况并不乐观。更多的同学是知道PHP-FPM、知道FastCGI,但不晓得Nginx、PHP这对老搭档具体的交互细节。那么,今天我们就来一起学习一下,做一回认真的PHP工程师。

前菜

为了讲解的有理有据,我们先来准备一个纯净精简的Nginx+PHP环境,这里我们使用Docker拉取Centos最新版本环境,来快速通过编译安装方式搭建一个Nginx+PHP环境。(图1,通过docker启动一台CentOS机器并进入)

docker

有了Linux环境,我们来源码编译安装Nginx、PHP,这个过程网络里有很多的教程,我们就不细说了。当然你也可以安装lnmp一键安装包来快速搭建。通过安装nginx、php,我们的Linux环境里就有了今天的这两位主角了。我们稍加配置,让Nginx可以接收请求并转发给PHP-FPM,我们目标是输出一个phpinfo()的信息。(图2,phpinfo()的输出内容)

php

我们通过对Nginx新增Server配置实现了nginx与PHP的一次通信,配置文件非常简单,如下图:(图3,一份nginx server配置)

nginx

有了上面的一个sample示例,我们开始深入Nginx与FastCGI协议。

主食

从上图的 Nginx 配置中可以注意到 fastcgi* 开头的一些配置,以及引入的 fastcgi.conf 文件。其实在 fastcgi.conf 中,也是一堆 fastcgi* 的配置项,只是这些配置项相对不常变,通常单独文件保管可以在多处引用。(图4,fastcgi.conf 文件中的内容)

可以看到在 fastcgi.conf 中,有很多的 fastcgi_param 配置,结合 nginx server 配置中的 fastcgi_passfastcgi_index,通常我们的同学已经能够想到 Nginx与PHP之间打交道就是用的 FastCGI,但再深问 FastCGI 是什么?它起到衔接Nginx、PHP的什么作用?等等深入的问题的时候,很多同学就卡壳了。

那么,我们就来一探究竟。CGI 是通用网关协议,FastCGI 则是一种常住进程的 CGI 模式程序。我们所熟知的 PHP-FPM的全称是 PHP FastCGI Process Manager,即 PHP-FPM 会通过用户配置来管理一批 FastCGI 进程,例如在PHP-FPM管理下的某个 FastCGI 进程挂了,PHP-FPM 会根据用户配置来看是否要重启补全,PHP-FPM更像是管理器,而真正衔接 Nginx 与 PHP 的则是 FastCGI 进程。(图5,FastCGI在请求流中的位置)

CGI

如上图所示,FastCGI 的下游,是 CGI-APP,在我们的 LNMP 架构里,这个 CGI-APP 就是 PHP 程序。而 FastCGI 的上游是 Nginx,他们之间有一个通信载体,即图中的 socket。在我们上文图3的配置文件中,fastcgi_pass 所配置的内容,便是告诉 Nginx 你接收到用户请求以后,你该往哪里转发,在我们图3中是转发到本机的一个 socket 文件,这里 fastcgi_pass 也常配置为一个
http 接口地址(这个可以在 php-fpm.conf 中配置)。而上图5中的 Pre-fork,则对应着我们 PHP-FPM 的启动,也就是在我们启动PHP-FPM时便会根据用户配置启动诸多FastCGI触发器(FastCGI Wrapper)。

对 FastCGI 在 Nginx+PHP 的模式中的定位有了一定了解后,我们再来了解下 Nginx 中为何能写很多 fastcgi_* 的配置项。这是因为 Nginx 的一个默认内置 module 实现了 FastCGI 的 Client。关于 Module ngx_http_fastcgi_module的详细文档可以查看这里: http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html 。我们关心一下我们图4中的这些 fastcgi_param 都是些什么吧,详细描述见下图。(图6,nginx 模块中 fastcgi_param 的介绍)

nginx 模块中 fastcgi_param 的介绍

从图6中可以看到,fastcgi_param所声明的内容,将会被传递给“FastCGI server”,那这里指的就是fastcgi_pass所指向的server,也就是我们Nginx+PHP模式下的PHP-FPM所管理的FastCGI进程,或者说是那个socket文件载体。这时,有的同学会问:“为什么PHP-FPM管理的那些FastCGI进程要关心这些参数呢?”,好问题,我们一起想想我们做PHP应用开发时候有没有用到 $_SERVER 这个全局变量,它里面包含了很多服务器的信息,比如包含了用户的IP地址。同学们不想想我们的PHP身处socket文件之后,为什么能得到远端用户的IP呢?聪明的同学应该注意到图4中的一个fastcgi_param配置 REMOTE_ADDR ,这不正是我们在PHP中用 $_SERVER[‘REMOTE_ADDR’] 取到的用户IP么。的确,Nginx这个模块里fastcgi_param参数,就是考虑后端程序有时需要获取Webserver外部的变量以及服务器情况,那么ngx_http_fastcgi_module就帮我们做了这件事。真的是太感谢它啦!

那么我们已经说清了FastCGI是个什么东东,并且它在Nginx+PHP中的定位。我们回到前面提出的问题,“它起到衔接Nginx、PHP的什么作用?”。

对PHP有一定了解的同学,应该会知道PHP提供SAPI面向Webserver来提供扩展编程。但是这样的方式意味着你要是自主研发一套Webserver,你就需要学习SAPI,并且在你的Webserver程序中实现它。这意味着你的Webserver与PHP产生了耦合。在互联网的大趋势下,一般大家都不喜欢看到耦合。譬如Nginx在最初研发时候也不是为了和PHP组成黄金搭档而研发的,相信早些年的Nginx后端程序可能是其他语言开发。那么解决耦合的办法,比较好的方式是有一套通用的规范,上下游都兼容它。那么CGI协议便成了Nginx、PHP都愿意接受的一种方式,而FastCGI常住进程的模式又让上下游程序有了高并发的可能。那么,FastCGI的作用是Nginx、PHP的接口载体,就像插座与插销,让流行的WebServer与“世界上最好的语言”有了合作的可能。

有了这些基础背景知识与他们的缘由,我们就可以举一反三的做更多有意思的事情。譬如我在前年曾实现了Java程序中按照FastCGI Client的方式(替代Nginx)与PHP-FPM通信,实现Java项目+PHP的一种组合搭配,解决的问题是Java程序一般来说在代码调整后需要编译过程,而PHP可以随时调整代码随时生效,那么让Java作为项目外壳,一些易变的代码由PHP实现,在需要的时候Java程序通过FastCGI与PHP打交道就好。这套想法也是基于对Nginx+PHP交互模式的理解之上想到的。

网络中也有一些借助FastCGI的尝试与实践,譬如《Writing Hello World in FCGI with C++》这篇文章,用C++实现一个FastCGI的程序,外部依然是某款Webserver来处理HTTP请求,但具体功能则有C++来实现,他们的中间交互同样适用的FastCGI。同学们有兴趣了也可以做些Geek尝试。(图7,C++实现一个FastCGI程序)

C++实现一个FastCGI程序

甜品

通过本文的讲解,我们希望让大家看到,Nginx+PHP的工程模式下,两位主角分工明确,Nginx负责承载HTTP请求的响应与返回,以及超时控制记录日志等HTTP相关的功能,而PHP则负责处理具体请求要做的业务逻辑,它们俩的这种合作模式也是常见的分层架构设计中的一种,在它们各有专注面的同时,FastCGI又很好的将两块衔接,保障上下游通信交互,这种通过某种协议或规范来衔接好上下游的模式,在我们日常的PHP应用开发中也有这样的思想落地,譬如我们所开发的高性能API,具体的Client到底是PC、APP还是某个其他程序,我们不关心,而这些PC、APP、第三方程序也不关心我们的PHP代码实现,他们按照API的规范来请求做处理即可。同学们是不是发现技术思想是可以在各个环节融会贯通的,是不是很兴奋?很刺激?哈,同学们开心就好,祝大家在工作学习过程中,能挖掘到更多的好知识,提升自己的同时造福身边小伙伴!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 1. Nginx的模块与工作原理 Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单...
    rosekissyou阅读 10,180评论 5 124
  • CGI 通用网关接口(Common Gateway Interface)是一个Web服务器主机提供信息服务的标准接...
    没我找不到电子书阅读 1,915评论 1 4
  • Nginx的工作原理 1.Nginx的模块与工作原理 Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,...
    架构飞毛腿阅读 5,994评论 1 27
  • 更改ip和dnsVi /etc/sysconfig/network-scripts/ifcfg-eth0vi /...
    Xwei_阅读 1,794评论 0 3
  • Nginx简介 解决基于进程模型产生的C10K问题,请求时即使无状态连接如web服务都无法达到并发响应量级一万的现...
    魏镇坪阅读 1,984评论 0 9