在讨论 FastCGI 之前,不得不说传统的 CGI 的工作原理,同时应该大概了解 CGI 1.1 协议
传统 CGI 工作原理分析
客户端访问某个 URL 地址之后,通过 GET/POST/PUT 等方式提交数据,并通过 HTTP 协议向 Web 服务器发出请求,服务器端的 HTTP Daemon(守护进程)将 HTTP 请求里描述的信息通过标准输入 stdin 和环境变量(environment variable)传递给主页指定的 CGI 程序,并启动此应用程序进行处理(包括对数据库的处理),处理结果通过标准输出 stdout 返回给 HTTP Daemon 守护进程,再由 HTTP Daemon 进程通过 HTTP 协议返回给客户端。
上面的这段话可能还是比较抽象,下面通过一次GET请求为例说明。
Web 服务器代码
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <string.h> #define SERV_PORT 9003char* str_join(char *str1, char *str2);char* html_response(char *res, char *buf); int main(void){ int lfd, cfd; struct sockaddr_in serv_addr,clin_addr; socklen_t clin_len; char buf[1024],web_result[1024]; int len; FILE *cin; if((lfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ perror("create socket failed"); exit(1); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERV_PORT); if(bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { perror("bind error"); exit(1); } if(listen(lfd, 128) == -1) { perror("listen error"); exit(1); } signal(SIGCLD,SIG_IGN); while(1) { clin_len = sizeof(clin_addr); if ((cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len)) == -1) { perror("接收错误\n"); continue; } cin = fdopen(cfd, "r"); setbuf(cin, (char *)0); fgets(buf,1024,cin); //读取第一行 printf("\n%s", buf); //============================ cgi 环境变量设置演示 ============================ // 例如 "GET /user.cgi?id=1 HTTP/1.1"; char *delim = " "; char *p; char *method, *filename, *query_string; char *query_string_pre = "QUERY_STRING="; method = strtok(buf,delim); // GET p = strtok(NULL,delim); // /user.cgi?id=1 filename = strtok(p,"?"); // /user.cgi if (strcmp(filename,"/favicon.ico") == 0) { continue; } query_string = strtok(NULL,"?"); // id=1 putenv(str_join(query_string_pre,query_string)); //============================ cgi 环境变量设置演示 ============================ int pid = fork(); if (pid > 0) { close(cfd); } else if (pid == 0) { close(lfd); FILE *stream = popen(str_join(".",filename),"r"); fread(buf,sizeof(char),sizeof(buf),stream); html_response(web_result,buf); write(cfd,web_result,sizeof(web_result)); pclose(stream); close(cfd); exit(0); } else { perror("fork error"); exit(1); } } close(lfd); return 0;}char* str_join(char *str1, char *str2){ char *result = malloc(strlen(str1)+strlen(str2)+1); if (result == NULL) exit (1); strcpy(result, str1); strcat(result, str2); return result;}char* html_response(char *res, char *buf){ char *html_response_template = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\nContent-Length: %d\r\nServer: mengkang\r\n\r\n%s"; sprintf(res,html_response_template,strlen(buf),buf); return res;}
CGI 程序(user.c)
#include <stdio.h>#include <stdlib.h>// 通过获取的 id 查询用户的信息int main(void){ //============================ 模拟数据库 ============================ typedef struct { int id; char *username; int age; } user; user users[] = { {}, { 1, "mengkang.zhou", 18 } }; //============================ 模拟数据库 ============================ char *query_string; int id; query_string = getenv("QUERY_STRING"); if (query_string == NULL) { printf("没有输入数据"); } else if (sscanf(query_string,"id=%d",&id) != 1) { printf("没有输入id"); } else { printf("用户信息查询<br>学号: %d<br>姓名: %s<br>年龄: %d",id,users[id].username,users[id].age); } return 0;}
将上面的 CGI 程序编译成 gcc user.c -o user.cgi ,放在上面web程序的同级目录。
FastCGI 工作原理分析
相对于 CGI/1.1 规范在 Web 服务器在本地 fork 一个子进程执行 CGI 程序,填充 CGI 预定义的环境变量,放入系统环境变量,把 HTTP body 体的 content 通过标准输入传入子进程,处理完毕之后通过标准输出返回给 Web 服务器。 FastCGI 的核心则是取缔传统的 fork-and-execute 方式,减少每次启动的巨大开销(后面以 PHP 为例说明),以常驻的方式来处理请求。
FastCGI 与传统 CGI 模式的区别在于 Web 服务器不是直接执行 CGI 程序了,而是通过 socket 与 FastCGI 响应器(FastCGI 进程管理器)进行交互,Web 服务器需要将 CGI 接口数据封装在遵循 FastCGI 协议包中发送给 FastCGI 响应器程序。正是由于 FastCGI 进程管理器是基于 socket 的,所以也是分布式的,Web服务器和CGI响应器服务器分开部署。
FastCGI 协议消息流程
在官方的介绍文档中列举了一些例子 http://www.fastcgi.com/devkit/doc/fcgi-spec.html#SB
下面我们从比较简单的流程入手理解,如下图所示
例如下面的例子
{FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, 0}}{FCGI_PARAMS, 1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}{FCGI_STDIN, 1, "quantity=100&item=3047936"}{FCGI_STDOUT, 1, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}{FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
FastCGI 请求的各个阶段在 PHP 中的定义
typedef enum _fcgi_request_type { FCGI_BEGIN_REQUEST = 1, /* [in] */ FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */ FCGI_END_REQUEST = 3, /* [out] */ FCGI_PARAMS = 4, /* [in] environment variables */ FCGI_STDIN = 5, /* [in] post data */ FCGI_STDOUT = 6, /* [out] response */ FCGI_STDERR = 7, /* [out] errors */ FCGI_DATA = 8, /* [in] filter data (not supported) */ FCGI_GET_VALUES = 9, /* [in] */ FCGI_GET_VALUES_RESULT = 10 /* [out] */} fcgi_request_type;
FastCGI 头在 PHP 里的定义
typedef struct _fcgi_header { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved;} fcgi_header;
字段解释
version 标识FastCGI协议版本。
type 标识FastCGI记录类型,也就是记录执行的一般职能。
requestId 标识记录所属的FastCGI请求。
contentLength 记录的contentData组件的字节数。
关于上面的 xxB1 和 xxB0 的协议说明:当两个相邻的结构组件除了后缀“B1”和“B0”之外命名相同时,它表示这两个组件可视为估值为B1
比如协议头中 requestId 和 contentLength 表示的最大值就是 65535
#include <stdio.h>#include <stdlib.h>#include <limits.h>int main(){ unsigned char requestIdB1 = UCHAR_MAX; unsigned char requestIdB0 = UCHAR_MAX; printf("%d\n", (requestIdB1 << 8) + requestIdB0); // 65535}
FCGI_BEGIN_REQUEST 在 PHP 里的定义
typedef struct _fcgi_begin_request { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5];} fcgi_begin_request;
Web服务器发送FCGI_BEGIN_REQUEST记录开始一个请求。
字段解释
role 表示Web服务器期望应用扮演的角色。分为三个角色在 PHP 里也有定义
typedef enum _fcgi_role { FCGI_RESPONDER = 1, FCGI_AUTHORIZER = 2, FCGI_FILTER = 3} fcgi_role;
flags 组件包含一个控制线路关闭的位: flags & FCGI_KEEP_CONN :如果为0,则应用在对本次请求响应后关闭线路。如果非0,应用在对本次请求响应后不会关闭线路;Web服务器为线路保持响应性。
FCGI_END_REQUEST 在 PHP 中的定义
typedef struct _fcgi_end_request { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3];} fcgi_end_request;
appStatus 组件是应用级别的状态码。
protocolStatus 组件是协议级别的状态码; protocolStatus 的值可能是:
FCGI_REQUEST_COMPLETE:请求的正常结束。
FCGI_CANT_MPX_CONN:拒绝新请求。这发生在Web服务器通过一条线路向应用发送并发的请求时,后者被设计为每条线路每次处理一个请求。
FCGI_OVERLOADED:拒绝新请求。这发生在应用用完某些资源时,例如数据库连接。
FCGI_UNKNOWN_ROLE:拒绝新请求。这发生在Web服务器指定了一个应用不能识别的角色时。
protocolStatus 在 PHP 中的定义如下
typedef enum _fcgi_protocol_status { FCGI_REQUEST_COMPLETE = 0, FCGI_CANT_MPX_CONN = 1, FCGI_OVERLOADED = 2, FCGI_UNKNOWN_ROLE = 3} dcgi_protocol_status;
需要注意 dcgi_protocol_status 和 fcgi_role 各个元素的值都是 FastCGI 协议里定义好的,而非 PHP 自定义的。

TheSecretTokeEpingAphp-PowerEdwebSiterUnningSmoothlyShyunderHeavyLoadInVolvOLVOLVOLDEVERSALKEYSTRATICES:1)emplactopCodeCachingWithOpcachingWithOpCacheToreCescriptexecution Time,2)使用atabasequercachingCachingCachingWithRedataBasEndataBaseLeSendataBaseLoad,3)

你應該關心DependencyInjection(DI),因為它能讓你的代碼更清晰、更易維護。 1)DI通過解耦類,使其更模塊化,2)提高了測試的便捷性和代碼的靈活性,3)使用DI容器可以管理複雜的依賴關係,但要注意性能影響和循環依賴問題,4)最佳實踐是依賴於抽象接口,實現鬆散耦合。

是的,優化papplicationispossibleandessential.1)empartcachingingcachingusedapcutorediucedsatabaseload.2)優化的atabaseswithexing,高效Quereteries,and ConconnectionPooling.3)EnhanceCodeWithBuilt-unctions,避免使用,避免使用ingglobalalairaiables,並避免使用

theKeyStrategiestosigantificallyBoostPhpaPplicationPerformenCeare:1)UseOpCodeCachingLikeLikeLikeLikeLikeCacheToreDuceExecutiontime,2)優化AtabaseInteractionswithPreparedStateTementStatementStatementAndProperIndexing,3)配置

aphpdepentioncontiveContainerIsatoolThatManagesClassDeptions,增強codemodocultion,可驗證性和Maintainability.itactsasaceCentralHubForeatingingIndections,因此reducingTightCightTightCoupOulplingIndeSingantInting。

選擇DependencyInjection(DI)用於大型應用,ServiceLocator適合小型項目或原型。 1)DI通過構造函數注入依賴,提高代碼的測試性和模塊化。 2)ServiceLocator通過中心註冊獲取服務,方便但可能導致代碼耦合度增加。

phpapplicationscanbeoptimizedForsPeedAndeffificeby:1)啟用cacheInphp.ini,2)使用preparedStatatementSwithPdoforDatabasequesies,3)3)替換loopswitharray_filtaray_filteraray_maparray_mapfordataprocrocessing,4)conformentnginxasaseproxy,5)

phpemailvalidation invoLvesthreesteps:1)格式化進行regulareXpressecthemailFormat; 2)dnsvalidationtoshethedomainhasavalidmxrecord; 3)


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver CS6
視覺化網頁開發工具