千家信息网

如何理解FastCGI协议

发表于:2024-11-29 作者:千家信息网编辑
千家信息网最后更新 2024年11月29日,今天就跟大家聊聊有关如何理解FastCGI协议,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。CGICGI全称是"公共网关接口"(Common
千家信息网最后更新 2024年11月29日如何理解FastCGI协议

今天就跟大家聊聊有关如何理解FastCGI协议,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

CGI

CGI全称是"公共网关接口"(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行"交谈"的一种工具,其程序须运行在网络服务器上。

cgi的弊端

cgi会产生什么问题呢?

当每一个请求进入的时候,cgi都会fork一个新的进程,然后以php为例,每个请求都要耗费相当大的内存,这样一来,并发起来,完全就会GG。

为了解决这个问题,于是产生了fastCgi。

FastCGI

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。

它还支持分布式的运算,即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。

一次完整的http经cgi请求到后端的分析

http request:

Request URL: http://demo.inke.cn/server.phpRequest Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgradeAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cache-Control: max-age=0Connection: keep-aliveCookie: aid=fa29caf3-5b18-401c-8abd-677eb1a1c45f; s-59804d897bc03a0036dc141c=2a126bda133d4feba4169fa70308f2c7Host: demo.inke.cnUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36

server.php

  string(6) "limars"  ["HOME"]=>  string(13) "/Users/limars"  ["HTTP_COOKIE"]=>  string(101) "aid=d11e3484-fd7d-4c6e-a2a5-bfabe2271da2; s-59804d897bc03a0036dc141c=fcb6a8cf23644f50b66e405d173eced7"  ["HTTP_ACCEPT_LANGUAGE"]=>  string(14) "zh-CN,zh;q=0.9"  ["HTTP_ACCEPT_ENCODING"]=>  string(13) "gzip, deflate"  ["HTTP_ACCEPT"]=>  string(118) "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"  ["HTTP_USER_AGENT"]=>  string(121) "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"  ["HTTP_UPGRADE_INSECURE_REQUESTS"]=>  string(1) "1"  ["HTTP_CONNECTION"]=>  string(10) "keep-alive"  ["HTTP_HOST"]=>  string(12) "demo.inke.cn"  ["REDIRECT_STATUS"]=>  string(3) "200"  ["SERVER_NAME"]=>  string(12) "demo.inke.cn"  ["SERVER_PORT"]=>  string(2) "80"  ["SERVER_ADDR"]=>  string(9) "127.0.0.1"  ["REMOTE_PORT"]=>  string(5) "51429"  ["REMOTE_ADDR"]=>  string(9) "127.0.0.1"  ["SERVER_SOFTWARE"]=>  string(12) "nginx/1.12.2"  ["GATEWAY_INTERFACE"]=>  string(7) "CGI/1.1"  ["REQUEST_SCHEME"]=>  string(4) "http"  ["SERVER_PROTOCOL"]=>  string(8) "HTTP/1.1"  ["DOCUMENT_ROOT"]=>  string(31) "/Users/limars/Desktop/inke/demo"  ["DOCUMENT_URI"]=>  string(11) "/server.php"  ["REQUEST_URI"]=>  string(11) "/server.php"  ["SCRIPT_NAME"]=>  string(11) "/server.php"  ["CONTENT_LENGTH"]=>  string(0) ""  ["CONTENT_TYPE"]=>  string(0) ""  ["REQUEST_METHOD"]=>  string(3) "GET"  ["QUERY_STRING"]=>  string(0) ""  ["SCRIPT_FILENAME"]=>  string(42) "/Users/limars/Desktop/inke/demo/server.php"  ["FCGI_ROLE"]=>  string(9) "RESPONDER"  ["PHP_SELF"]=>  string(11) "/server.php"  ["REQUEST_TIME_FLOAT"]=>  float(1565082651.4024)  ["REQUEST_TIME"]=>  int(1565082651)}

它是个怎样的转换过程呢?仔细观察,我们会发现有很多东西是http协议里没有的,那这些东西是怎么生成的呢?

观察nginx配置:

listen       80;server_name  demo.inke.cn;root        /Users/limars/Desktop/inke/demo;  location ~ \.php$ {            try_files        $uri =404;            fastcgi_pass 127.0.0.1:9000;             fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;            include        fastcgi_params;}

我们会发现一个叫 fastcgi_param的可配置参数,而nginx一般提供了默认值。 打开nginx目录下的fastcgi_params文件可以看到:

fastcgi_param  QUERY_STRING       $query_string;fastcgi_param  REQUEST_METHOD     $request_method;fastcgi_param  CONTENT_TYPE       $content_type;fastcgi_param  CONTENT_LENGTH     $content_length;fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;fastcgi_param  REQUEST_URI        $request_uri;fastcgi_param  DOCUMENT_URI       $document_uri;fastcgi_param  DOCUMENT_ROOT      $document_root;fastcgi_param  SERVER_PROTOCOL    $server_protocol;fastcgi_param  REQUEST_SCHEME     $scheme;fastcgi_param  HTTPS              $https if_not_empty;fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;fastcgi_param  REMOTE_ADDR        $remote_addr;fastcgi_param  REMOTE_PORT        $remote_port;fastcgi_param  SERVER_ADDR        $server_addr;fastcgi_param  SERVER_PORT        $server_port;fastcgi_param  SERVER_NAME        $server_name;

这时候,我们对比下$_SERVER中http_XXX_XXX的,这不是都是http自带的头部嘛?

综合下来,这就是一个http请求完整转发到php的过程,一次完整的cgi请求,而fastcgi呢,就是对cgi协议的常驻封装,主要解决的问题是:

优化性能(cgi不停fork新进程,每个进程都干很多同样的事【加载配置文件、加载扩展】) 进程池维护,异步模型,请求是分发到空闲子进程,方便创建/销毁进程。

fcgi协议组成(go语言描述)

fcgi共12种消息类型

const ( typeBeginRequest uint8 = 1 typeAbortRequest uint8 = 2 typeEndRequest uint8 = 3 typeParams uint8 = 4 typeStdin uint8 = 5 typeStdout uint8 = 6 typeStderr uint8 = 7 typeData uint8 = 8 typeGetValues uint8 = 9 typeGetValuesResult uint8 = 10 typeUnknownType uint8 = 11)

见名知意:

1~3是信号类型

4~10是不同的数据类型

11、default 是错误类型

数据传输

通常协议传输,为了考虑数据大小的原因,都会将数据进行分片处理,fcgi也同样。

fcgi协议的每个包都由包头和包体组成

//It's fcgi headertype header struct {        Version       uint8   Type          uint8   Id            uint16   ContentLength uint16   PaddingLength uint8   Reserved      uint8}

包的正文,就是由分割的二进制内容组成,一个包最多传输2^16次方减一,也就是65535个字节。

一次完整的请求过程

请求:解析http请求获取配置->发送cgi请求->typeBeginRequest -> write Params -> write Stdin(if exists) -> write Data (if exists)

//写头部if request.KeepConn {   //if it's keep-alive   //set flags 1   err = cgi.writeBeginRequest(reqId, roleResponder, 1)} else {   err = cgi.writeBeginRequest(reqId, roleResponder, 0)} if err != nil {   return} //写http headererr = cgi.writePairs(typeParams, reqId, request.Params)if err != nil {   return}  //读取http body并写到cgip := make([]byte, 1024)n, _ := request.Stdin.Read(p)err = cgi.writeRecord(typeStdin, reqId, p[:n])//写其他信息 //写最后一个换行err = cgi.writeRecord(typeStdin, reqId, nil)

接收:check response type-> 错误处理(stderr、unknownType、default)->正确类型->接受数据复用socket返回给请求方。 rec := &record{} //建立一个记录缓冲

    var err1 error     // recive untill EOF or FCGI_END_REQUEST    for {        err1 = rec.read(cgi.rwc)   //从cgi建立的链接读取数据        if err1 != nil {           //判断是否有错误,判断错误是否为终止符            if err1 != io.EOF {  // keepalive on的时候 不会有终止符  这个要注意,读完长度为content-length即当前请求返回                err = err1            }            break        }        switch {  //根据返回的类型,将内容写到响应的字节数组中        case rec.h.Type == typeStdout:              retout = append(retout, rec.content()...)        case rec.h.Type == typeStderr:            reterr = append(reterr, rec.content()...)        case rec.h.Type == typeEndRequest:            fallthrough        default:            break        }    }

看完上述内容,你们对如何理解FastCGI协议有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

0