CGI 和 法斯特CGI 左券的运作规律

在研究 法斯特CGI 以前,一定要说古板的 CGI
的行事规律,同临时间应该差十分少掌握 CGI
1.1 协议

Web 服务器和 法斯特CGI 交互作用进程

  • Web 服务器收到客商诉求,但最后管理央浼由 Web 应用完结。此时,Web
    服务器尝试通过套接字(UNIX 或 TCP 套接字,具体应用哪个由 Web
    服务器配置决定)连接到 法斯特CGI 进度。

  • 法斯特CGI
    进度查看选拔到的接连几日。接受「采纳」或「推却」连接。若是是「选拔」连接,则从标准输入流中读取数据包。

  • 固然 法斯特CGI
    进度在指依时期内还未有得逞接到到连年,则该央求退步。不然,Web
    服务器发送贰个包涵唯豆蔻年华的RequestID 的 BEGIN_REQUEST 类型音讯给到
    法斯特CGI 进度。后续全数数据包发送都满含这些 RequestID。 然后,Web
    服务器发送任意数量的 PARAMS 类型音讯到 法斯特CGI
    进度。生龙活虎旦发送实现,Web 服务器通过发送三个空PARAMS
    音讯包,然后关门这么些流。 此外,假如客商发送了 POST 数据 Web
    服务器会将其写入到 规范输入 发送给 FastCGI 进程。当全部 POST
    数据发送实现,会发送一个空的 标准输入 来关闭那几个流。

  • 再正是,法斯特CGI 进度选取到 BEGINREQUEST 类型数据包。它能够由此响应
    ENDREQUEST
    来回绝这些须求。或许收受并拍卖这一个恳求。倘诺选拔央浼,法斯特CGI
    进度会等待选择全数的 PARAMS 和 规范输入数据包。
    然后,在管理央求并将回到结果写入 规范输出
    流。处理到位后,发送二个空的数目包到正式输出来关闭那么些流,况且会发送三个END_REQUEST 类型消息文告 Web 服务器,告知它是不是爆发错误特别。

5 总结

从CGI,FastCGI,WSGI到uWSGI,涉及内容很底工也很混乱,本文只是一得之见,说出了本身对那个概念的为主理解,如有错漏,恳请指正。实际项目中用到的构造如
nginx + uWSGI + Flask
(合营gevent,mysql线程池等),前边偶然光再做计算。

在运转阶段,Apache重要专门的学问是拍卖客户的劳务央求,在这里个阶段Apache以普通客户运维。首若是安全性考虑,Apache对HTTP的伸手能够分为连接、管理和断开连接两个大的阶段。

法斯特CGI 音讯类型

法斯特CGI 将传输的音讯做了不菲类其余细分,其结构体定义如下:

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;

CGI 与 FastCGI 架构

在 CGI 协商中,Web 应用的生命周期完全信赖于 HTTP 央求的表明周期。

澳门大赌坊,对各样接受到的 HTTP 央求,都亟待重启一个 CGI
进度来开展拍卖,管理到位后必得关闭 CGI 进程,才具完毕公告 Web 服务器本次HTTP 央浼管理完了的目标。

不过在 法斯特CGI 中完全不相符。

法斯特CGI 进度是常驻型的,生龙活虎旦运营就能够拍卖全数的 HTTP
央求,而无需直接退出。

3 WSGI

(2)php中CGI实现

FCGI_END_REQUEST 的定义

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_OVE悍马H2LOADED:谢绝新央浼。那发生在动用用完有些财富时,举个例子数据库连接。
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_statusfcgi_role逐后生可畏要素的值都以 法斯特CGI
左券里定义好的,而非 PHP 自定义的。

深入CGI协议

我们早就通晓了 CGI 合同是为着酿成 Web
服务器和选用之间进行数量通讯那么些标题。那么,这黄金年代节大家就来探问到底它们之间是怎么着进行通讯的。

简短来说 CGI 公约它描述了 Web
服务器和应用程序之间实行数量传输的格式,并且只要大家的编制程序语言援救标准输入、标准输出以至情况变量等拍卖,你就足以选取它来编排叁个CGI 程序。

6 参考资料

  • https://tools.ietf.org/html/rfc3875
  • http://www.nongnu.org/fastcgi/\#id2860531
  • http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html
  • https://superuser.com/questions/484671/can-i-monitor-a-local-unix-domain-socket-like-tcpdump
  • https://mengkang.net/668.html
  • http://www.garshol.priv.no/download/text/http-tut.html\#app
  • https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/
  • http://www.mike.org.cn/articles/what-is-cgi-fastcgi-php-fpm-spawn-fcgi/
  • https://github.com/toshic/libfcgi
  • https://www.python.org/dev/peps/pep-3333/
  • https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
  • https://uwsgi-docs.readthedocs.io/en/latest/Protocol.html

(1卡塔尔国cgi是通用网关接口(Common Gateway
IntedfaceState of Qatar,它能够让二个顾客端从网页浏览器向施行在Web服务器上的程序央浼数据。CGI描述了顾客端和那个顺序之间传输数据的正经八百。CGI的叁个指标是单独于任何语言,所以CGI能够用别的语言编写,只要这种语言具备专门的学问输入、输出和情况变量。如PHP、perl、tcl等。

消息广播发表样例

为了轻巧的代表,信息头只呈现新闻的等级次序和音信的
id,别的字段都不付与体现。上面包车型地铁事例来自于官方网站

{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}}

相称地点各样构造体,则能够大概想到 法斯特CGI 响应器的剖判和响应流程:

第生龙活虎读废除息头,获得其品种为FCGI_BEGIN_REQUEST,然后深入分析其消息体,获悉其必要的角色正是FCGI_RESPONDERflag为0,表示央求甘休后关门线路。然后分析第二段音信,得到消息其音讯类型为FCGI_PARAMS,然后间接将音讯体里的开始和结果以回车符切割后存入情状变量。与之相近,处理完结之后,则赶回了FCGI_STDOUT新闻体和FCGI_END_REQUEST新闻体供
Web 服务器解析。

再看 FastCGI 协议

经过前边的批注,大家相比较已经足以很标准的说出去 法斯特CGI
是蓬蓬勃勃种通讯左券

那样的下结论。今后,大家就将关爱的抢手挪到协商本人,来探视这些协议的概念。

同 HTTP 左券同样,法斯特CGI 左券也许有新闻头和音信体组成。

2 FastCGI协议

TCP上顾客-服务器业务的时序如图所示:

5.读取数据

下边包车型客车代码删除一些极度情状的管理,只浮现了经常意况下进行顺序。

fcgi_read_request中则实现大家在音讯广播发表样例中的新闻读取,而个中许多的len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;操作,已经在前边的法斯特CGI
新闻头中解释过了。

那边是剖判 FastCGI 左券的要害。

static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count)
{
    int    ret;
    size_t n = 0;

    do {
        errno = 0;
        ret = read(req->fd, ((char*)buf)+n, count-n);
        n += ret;
    } while (n != count);
    return n;
}

static int fcgi_read_request(fcgi_request *req)
{
    ...

    if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || hdr.version < FCGI_VERSION_1) {
        return 0;
    }

    len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
    padding = hdr.paddingLength;

    req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;

    if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
        char *val;

        if (safe_read(req, buf, len+padding) != len+padding) {
            return 0;
        }

        req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);

        switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
            case FCGI_RESPONDER:
                val = estrdup("RESPONDER");
                zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
                break;
            ...
            default:
                return 0;
        }

        if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || hdr.version < FCGI_VERSION_1) {
            return 0;
        }

        len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
        padding = hdr.paddingLength;

        while (hdr.type == FCGI_PARAMS && len > 0) {
            if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || hdr.version < FCGI_VERSION_1) {
                req->keep = 0;
                return 0;
            }
            len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
            padding = hdr.paddingLength;
        }

        ...
    }
}

CGI的运转规律

  • 当客商访谈大家的 Web 应用时,会发起二个 HTTP 伏乞。最后 Web
    服务器收到到那么些哀告。

  • Web 服务器创建三个新的 CGI 进度。在此个进程中,将 HTTP
    央求数据已无可置疑格式深入拆解分析出来,并经过正式输入和遭逢变量传入到 U翼虎L
    钦命的 CGI 程序(PHP 应用 $_SERVER)。

  • Web 应用程序管理完了后将回到数据写入到正规输出中,Web
    服务器进程则从标准输出流中读取到响应,并应用 HTTP
    合同重临给客商响应。

一句话就是 Web 服务器中的 CGI 进度将选用到的 HTTP
诉求数据读取到情状变量中,通过标准输入转载给 PHP 的 CGI 程序;当 PHP
程序管理实现后,Web 服务器中的 CGI
进度从正式输出中读取重返数据,并转移回 HTTP
响应音信格式,最终将页面呈献给客户。然后 Web 服务器关闭掉那些 CGI 进度。

能够说 CGI 公约极其擅长管理 Web 服务器和 Web
应用的通讯难题。可是,它有二个严重缺欠,对于每种要求都亟待重新 fork
出一个 CGI 进程,管理到位后登时关闭。

WEB服务器发送给法斯特CGI程序的多少包:

  • 第二个新闻是
    BEGIN_REQUEST,能够看出首个字节为01,也等于version为1,第三个字节为01,即音信类型是
    BEGIN_REQUEST,接着3-4字节0001是requestId为1。再接着5-6字节0008是音讯体长度为8。然后7-8字节0000是保留字段和填充字段。接着8个字节正是音信体了,9-10字节0001为role值,表示FCGI_RESPONDER,也等于那是四个索要响应的音讯。11字节00为flag,表示应用在这里次央求后关门连接。然后12-16的5个字节0000000000为保存字段。

  • 其次个音讯的第四个字节是01,也是version为1,第四个字节为04,表示消息类型为PARAMS。接着3-4字节为0001是requestId也是1。5-6字节0x0392音信体长度为914字节。前面7-8是0600位填充字段6字节。前面包车型地铁为新闻体内容,也正是QUERY_STRING, REQUEST_METHOD这个在CGI中装置到意况变量中的变量和值。接下来是PARAMS新闻体。PARAMS新闻用的是Name-Value对这种样式组织的数据构造,先是变量名称长度,然后是变量值长度,接着才是名字和值的实际数量。注意,名和值的尺寸假使超过1字节,则用4个字节来存款和储蓄,具体是1字节或然4字节依照长度值的率先个字节的参天位来差异,如若为1则是4字节,即便为0则是1字节。如此能够剖析PARAMS新闻体了,头两个字节0c07表示名字长度为12,值长度为7,然后正是10个字节的变量名QUERY_STRING,7字节的值foo=bar,就那样推算,接着的2个字节0e03正是名字长度为14,值长度为3,变量名是REQUEST_METHOD,值为GET…后续数据就是剩下的别样变量。最终边的6个字节000000000000是填充字节。

  • 其多个信息也是PARAMS,那是叁个空的PARAMS音讯。第1字节为01,第2字节为04象征PARAMS,3-4字节0001是requestId为1,5-6字节0000代表音讯体长度为0,7-8字节0000表示填充和保存字节为0。

  • 第三个消息为STDIN,第三个字节01是version,第4个字节05表示项目为STDIN,接下去是3-4字节0001是requestId为1,5-6字节表示音讯体长度为0,因为我们从未POST数据。前面7-8字节为0。(如若有POST数据,则STDIN这里音信体长度不为0,而它的消息体正是POST的多寡,注意STDIN不是Name-Value对,它是平昔将POST的数据字段连在一同的,如那样id=1&name=ssj)。到此,WEB服务器发送给法斯特CGI程序的多寡包截至。

这几个主意都归于Apache服务器,以读取cookie为例,当大家在Apache服务器景况下,在PHP中调用读取Cookie时,最后得到的多寡的职责是在激活SAPI时,它所调用的办法是read_cookie。

4.在子进度中选取央求

到此地全部都依旧 socket
的服务的覆辙。选拔央浼,然后调用了fcgi_read_request

fcgi_accept_request(&request)

int fcgi_accept_request(fcgi_request *req)
{
    int listen_socket = req->listen_socket;
    sa_t sa;
    socklen_t len = sizeof(sa);
    req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);

    ...

    if (req->fd >= 0) {
        // 采用多路复用的机制
        struct pollfd fds;
        int ret;

        fds.fd = req->fd;
        fds.events = POLLIN;
        fds.revents = 0;
        do {
            errno = 0;
            ret = poll(&fds, 1, 5000);
        } while (ret < 0 && errno == EINTR);
        if (ret > 0 && (fds.revents & POLLIN)) {
            break;
        }
        // 仅仅是关闭 socket 连接,不清空 req->env
        fcgi_close(req, 1, 0);
    }

    ...

    if (fcgi_read_request(req)) {
        return req->fd;
    }
}

并且把request归入全局变量sapi_globals.server_context,那点很要紧,方便了在别的地点对须要的调用。

SG(server_context) = (void *) &request;

CGI和睦的弱点

  • 每趟处理客户央浼,都亟待重新 fork CGI 子进度、销毁 CGI 子进度。

  • 生龙活虎层层的 I/O
    开销裁减了网络的吞吐量,变成了能源的浪费,在大并发时会时有发生严重的品质难点。

景况变量

environ字典中必得带有CGI标准中定义的变量,满含上边那些:

  • REQUEST_METHOD
  • SCRIPT_NAME
  • PATH_INFO
  • QUERY_STRING
  • CONTENT_TYPE
  • CONTENT_LENGTH
  • SERVER_NAME, SERVER_PORT
  • SERVER_PROTOCOL
  • HTTP_Variables

除了CGI定义的境况变量之外,environ字典中还要包含下边多少个变量:

  • wsgi.version:WSGI版本,元组(1,0卡塔尔国表示版本为1.0.
  • wsgi.url_scheme:UEscortL形式,值常常为http可能https。
  • wsgi.input:可以读取HTTP诉求体的输入流。(当被使用对象哀告时,服务器/网关实施read
    ,能够预读取诉求体,缓存到内部存储器照旧磁盘中,也许用任哪里理输入流的技艺)
  • wsgi.errors:错误输出流。在诸多服务器中,wsgi.errors日常是服务器的日志。
  • wsgi.multithread:应用对象固然支持多线程,则设置为true。
  • wsgi.multiprocess:应用对象若是扶持多进度,则设置为true。
  • wsgi.run_once:要是服务器/网关希望选拔对象在含有它的进程中仅试行一回那几个必要,它的值为true。寻常情况下,唯有是基于CGI的网关才是true。

最后,environ辞书也说倒霉含有服务器定义的变量。那几个变量只好利用小写字母,数字,点和下划线来命名,并且应该用该服务器/网关唯大器晚成的名称作为前缀。比方,mod_python或者会定义名称叫mod_python.some_variable的变量。

对于每三个服务器在加载时,大家都内定了sapi_module,而Apache的sapi_module是apache2_sapi_module。在那之中对应read_cookie的方式是php_apache_sapi_read_cookie函数。那也是定义SAPI构造的说辞:统一接口,面向接口编制程序,具有更加好的扩大性和适应性。

8.专门的学业输入规范输出的拍卖

专门的工作输入和行业内部输出在地点没有同步研商,实际在cgi_sapi_module构造体中有定义,可是cgi_sapi_module这个sapi_module_struct构造体与此外轮代理公司码耦合太多,笔者自个儿也没浓郁的接头,这里大致做下比较,希望此外网上老铁授予教导、补充。

cgi_sapi_module中定义了sapi_cgi_read_post来拍卖POST数据的读取.

while (read_bytes < count_bytes) {
    fcgi_request *request = (fcgi_request*) SG(server_context);
    tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
    read_bytes += tmp_read_bytes;
}

fcgi_read中则对FCGI_STDIN的多寡开展读取。
同时cgi_sapi_module中定义了sapi_cgibin_ub_write来接管道输送出管理,而里面又调用了sapi_cgibin_single_write,最终实现了FCGI_STDOUT 法斯特CGI
数据包的封装.

fcgi_write(request, FCGI_STDOUT, str, str_length);

PHP-FPM

PHP-FPM即PHP-FastCGI Process Manager.

PHP-FPM是法斯特CGI的完毕,并提供了经过处理的效果。

经过包蕴 master 进度和 worker 进度三种进度。

master 进程唯有叁个,负担监听端口,采纳来自 Web Server 的央求,而 worker
进度则相近有八个(具体数目依据实际须求配备卡塔尔(قطر‎,每一种进程之中都停放了一个 PHP
解释器,是 PHP 代码真正实践的地点。

PHP-FPM 是 法斯特CGI 进度微机(PHP 法斯特CGI Process
Manager)( PHP 内核的
法斯特CGI 的大多数附加作用(恐怕说风度翩翩种代替的 PHP 法斯特CGI
达成),对于高负载网址是相当有效的。

应用/框架

利用对象正是多少个经受五个参数的可调用对象,它能够是函数,方法,类等。应用对象必须能够被频仍调用。纵然大家称为应用对象,但那并不意味着应用开拓者要用WSGI作为WEB编制程序API。应用开荒者能够三番五次运用已经存在的、高端框架服务去支付他们的行使。WSGI
是二个为框架开垦者和服务器开采者绸缪的工具,应用开拓者无需直接选取WSGI。

app.py是带有五个应用对象的亲自去做,此中八个是用函数完毕,另多个是用类实现。

 if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||
        ...
        bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||
        listen(listen_socket, backlog) < 0) {
        ...
    }

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程序的同级目录。

代码中的第28行,从境况变量中读取前面在Web服务器守护进程中装置的碰到变量,是大家演示的主要。

PHP-FPM如何是好事的?

PHP-FPM 进度微型机有三种进度组成,一个 Master 进程和八个 Worker
进度。Master 进度负担监听端口,选拔来自 Web 服务器的倡议,然后指使具体的
Worker 进程管理央浼;worker 进度则日常常有多个(依附配置决定进度数卡塔尔,每一种进程之中都放到了一个 PHP 解释器,用来实行 PHP
代码。

处理Content-Length头部

借使应用程序提供Content-Length头,则服务器不该发送比Content-Length越来越多的字节,而且应该在发送完Content-Length字节后终止发送响应,要是应用程序那个时候还继续尝试写入,则应当抛出荒诞)。

比如应用程序不提供Content-Length底部,则服务器或网关能够选取两种方法之一来管理它。最简易的是在响应实现时关闭顾客端连接。然而,在好几景况下,服务器/网关能够生成多个Content-Length尾部来制止关闭客户端连接。只顾:应用程序和中间件的输出中无法应用别的项指标Transfer-Encoding,举个例子chunking或gzip,这么些传输编码是Web服务器/网关的职分

这是mod_php5模块定义的指令表。它其实是两个commond_rec构造的数组。当Apache境遇指令的时候将依次遍历各样模块中的指令表,查找是不是有那三个模块能够管理该指令,假若找到,则调用响应的处理函数,借使具有指令表中的模块都不能够管理该指令,那么将报错,如上所见,mod_php5模块仅提供php_value等5个指令。

1.开启二个 socket 监听服务

fcgi_fd = fcgi_listen(bindpath, 128);

从此将来间早先监听,而fcgi_listen函数里面则完毕 socket
服务前三步socket,bind,listen

音信类型定义

  • BEGIN_REQUEST: 从 Web 服务器发送到 Web
    应用,表示起头拍卖新的恳求。

  • ABORT_REQUEST: 从 Web 服务器发送到 Web
    应用,表示暂停三个甩卖中的乞求。举个例子,客商在浏览器发起倡议后按下浏览器上的「结束开关」时,会触发这么些消息。

  • END_REQUEST: 从 Web 应用发送给 Web
    服务器,表示该要求管理达成。重返数据包里带有「重返的代码」,它决定央浼是不是中标拍卖。

  • PARAMS: 「流数据包」,从 Web 服务器发送到 Web
    应用。那时能够发送三个数据包。发送甘休标志为从 Web
    服务器发出一个长短为 0 的空包。且 PARAMS 中的数据类型和 CGI
    公约豆蔻梢头致。即大家应用 $_SETiggoVECR-V 获取到的种类意况等。

  • STDIN: 「流数据包」,用于 Web 应用从标准输入中读抽出客户提交的
    POST 数据。

  • STDOUT: 「流数据报」,从 Web
    应用写入到正式输出中,包涵重临给顾客的数目。

4 uWSGI和uwsgi协议

 

法斯特CGI 专业规律解析

周旋于 CGI/1.1 规范在 Web 服务器在当地 fork 四个子历程推行 CGI
程序,填充 CGI 预订义的情况变量,放入系统情况变量,把 HTTP body 体的
content 通过正式输入传入子进程,管理完结之后经过标准输出重返给 Web
服务器。法斯特CGI 的骨干则是禁止古板的 fork-and-execute
格局,收缩每回运行的宏伟开支(前面以 PHP
为例表达),以常驻的方法来管理诉求。

法斯特CGI 职业流程如下:

  1. 法斯特CGI 进度微型机本人早先化,运转多少个 CGI 解释器进程,并等待来自
    Web Server 的连续几天。
  2. Web 服务器与 法斯特CGI 进度微型机实行 Socket 通讯,通过 法斯特CGI
    商量发送 CGI 情形变量和正式输入数据给 CGI 解释器进度。
  3. CGI 解释器进度达成管理后将行业内部输出和错误消息从同一而再接再次来到 Web
    Server。
  4. CGI 解释器进度接着等待并管理来自 Web Server 的下一个三番两遍。

澳门大赌坊 1

法斯特CGI 与金钱观 CGI 情势的分别之一则是 Web 服务器不是一直实施 CGI
程序了,而是经过 socket 与 法斯特CGI 响应器(法斯特CGI
进度微型机)进行互相,Web 服务器须要将 CGI 接口数据封装在安份守己 FastCGI
合同包中发送给 法斯特CGI 响应器程序。正是出于 法斯特CGI 进程微机是依据socket 通讯的,所以也是布满式的,Web服务器和CGI响应器服务器分开陈设。

再啰嗦一句,法斯特CGI
是风姿洒脱种左券,它是白手成家在CGI/1.1幼功之上的,把CGI/1.1里头的要传送的多少经过法斯特CGI公约定义的顺序、格式实行传递。

FastCGI左券运转规律

  • 法斯特CGI 进程微型机运维时会创建叁个 主 进度和多少个 CGI
    解释器进度(Worker 进度),然后等待 Web 服务器的接连。

  • Web 服务器收到 HTTP 央求后,将 CGI 报文通过 套接字(UNIX 或 TCP
    Socket)进行通信,将情形变量和央求数据写入规范输入,转载到 CGI
    解释器进度。

  • CGI 解释器进度实现处理后将标准输出和错误信息从同三番四次接重返给 Web
    服务器。

  • CGI 解释器进度等待下一个 HTTP 央求的过来。

start_response

start_response是传递给选择对象的第3个参数是可调用对象(常常正是个函数),start_response(status,response_headers,exc_info
= NoneState of Qatar。
(与持有的WSGI可调用对象参数一样,这里不可不是岗位参数,无法用关键字参数)。start_response用于最先HTTP响应,它必须再次回到三个write(body_data卡塔尔国的可调用对象。

status参数正是”200 OK”恐怕”404 Not
Found”这种情状字符串,由状态码和境况表达结合的字符串,由一个空格分开,未有附近的空格或其余字符(越多请参见MuranoFC2616第6.1.1节)。字符串不可能包罗调整字符,也不可能以回车、换行符或它们的咬合甘休。

response_headers参数是(header_name,header_value)元组的列表。它必须是叁个Python列表类型,並且服务器能够随便改正其剧情。各类header_name必需是二个立见成效的HTTP底部字段名称(由WranglerFC2616,第4.2节定义)。header_nameheader_value不可能包蕴其余调整字符(包含回车符或换行符)。服务器/网关担负确认保证向客户端发送准确的响应底部:如若使用对象省略了HTTP响应所需的底部,则服务器/网关必需加多它。举个例子,HTTP的Date和Server尾部常常由服务器/网关提供。(注意:HTTP底部字段不区分轻重缓急写,由此在自己议论应用程序提供的底部时必须思索那一点!)。防止行使对象使用
HTTP 1.1的 hop-by-hop 性格可能头(如Keep-AliveState of Qatar,以至别的在
HTTP/1.0中也正是的表征,或任何影响客商端到 web服务器端漫长化连接的头顶。

服务器应该在调用start_response的时等候检查查头文件中的错误,以便应用程序仍在运营时抛出乖谬。不过,start_response实质上并不传输响应头。相反,它必需将它们存款和储蓄在服务器/网关上,以便仅在选拔回到值时也许在采用第一遍调用write(卡塔尔(قطر‎时传输。响应头传输的这种延迟是为了保证缓冲和异步应用程序能够用错误输出来替换它们原来预期的出口,直到最后的或是每日。比方,假若在应用程序缓冲区内生成正文时发生错误,则应用程序恐怕须求将status从“200
OK”改良为“500 Internal Error”。

exc_info参数(借使提供)必需是Python sys.exc_info()元组。唯有在错误管理程序调用start_response的事态下,应用程序能力提供此参数。假诺提供了exc_info,何况还从未发送HTTP头,start_response相应用新提供的底部替换当前囤积的HTTP底部,进而允许应用程序在发出错误时“改动主意”。可是,若是当时意气风发度发送了HTTP尾部,则start_response必需重新抛出格外。

static sapi_module_struct apache2_sapi_module = {
"apache2handler",
"Apache 2.0 Handler",

php_apache2_startup,                /* startup */
php_module_shutdown_wrapper,            /* shutdown */

NULL,                       /* activate */
NULL,                       /* deactivate */

php_apache_sapi_ub_write,           /* unbuffered write */
php_apache_sapi_flush,              /* flush */
php_apache_sapi_get_stat,           /* get uid */
php_apache_sapi_getenv,             /* getenv */
php_error,                  /* error handler */

php_apache_sapi_header_handler,         /* header handler */
php_apache_sapi_send_headers,           /* send headers handler */
NULL,                       /* send header handler */

php_apache_sapi_read_post,          /* read POST data */
php_apache_sapi_read_cookies,           /* read Cookies */

php_apache_sapi_register_variables,
php_apache_sapi_log_message,            /* Log message */
php_apache_sapi_get_request_time,       /* Request Time */
NULL,                       /* Child Terminate */

STANDARD_SAPI_MODULE_PROPERTIES
};

法斯特CGI 协议深入分析

上边结合 PHP 的 法斯特CGI 的代码实行拆解剖判,不作特殊表明以下代码均来源于于 PHP
源码。

Nginx 服务器怎么样与 法斯特CGI 合营工作

Nginx 服务器不能够直接与 法斯特CGI 服务器进行通讯,须要启用
ngx_http_fastcgi_module 模块实行代理配置,本领将央求发送给 法斯特CGI
服务。

转载:

PHP和Apache是如何通讯的?

Nginx+PHP-FPM运转原理详细明白

支配CGI和法斯特CGI合同的运作规律

中间件:能够扮演三种角色

中间件是那样风姿罗曼蒂克种对象,它不仅能够看做劳动器端跟应用端交互,也能够用作利用端跟服务器端交互作用。中间件组件平常兼有下边几个效果与利益:

  • 在重写了情况变量后,依据目的UPAJEROL将伏乞路由到分化的施用对象。
  • 同意四个应用或框架在同三个进度中相继奉行。
  • 因而转账号召和响应,帮衬负载均衡和长途管理。
  • 支撑对剧情进行持续管理。

中间件的留存对于接口的“服务器/网关”和“应用/框架”这两端是透明的,并无需非常的支持。大非常多气象下,中间件必得切合WSGI的服务器和应用程序端的节制和供给。

 

FCGI_BEGIN_REQUEST 的定义

typedef struct _fcgi_begin_request {
    unsigned char roleB1;
    unsigned char roleB0;
    unsigned char flags;
    unsigned char reserved[5];
} fcgi_begin_request;

字段解释

role意味着Web服务器期待利用扮演的剧中人物。分为多个剧中人物(而小编辈那边斟酌的图景平日都以响应器角色)

typedef enum _fcgi_role {
    FCGI_RESPONDER    = 1,
    FCGI_AUTHORIZER    = 2,
    FCGI_FILTER        = 3
} fcgi_role;

FCGI_BEGIN_REQUEST中的flags零器件包涵一个说了算线路关闭的位:flags & FCGI_KEEP_CONN:如若为0,则使用在对这一次央浼响应后关门线路。假设非0,应用在对此次央求响应后不会关闭线路;Web服务器为线路保持响应性。

新闻头音讯

首要的音讯头新闻如下:

  • Version: 用于表示 法斯特CGI 公约版本号。

  • Type: 用于标记 FastCGI 音讯的品类 – 用于钦赐管理那几个消息的法子。

  • RequestID: 标记出脚下所属的 法斯特CGI 央求。

  • Content Length: 数据公文包体所占字节数。

1.2 CGI实现

在具体应用中,WEB服务器常用的有nginx和apache。apache提供了好些个模块,能够一直加载CGI程序,和上生机勃勃章提到的办法基本豆蔻梢头致。而nginx是不能加载CGI程序的,必得其它单独运转一个CGI程序微处理器来管理CGI乞求,先来看下CGI实现,WEB服务器代码cgi.c。编写翻译并运营:

$ gcc -o cgi cgi.c
$ ./cgi

CGI程序如下,可感到C语言编写,如
cgi_hello.c,也得以是shell,python等其余语言,如
cgi_hello.sh。编译cgi_hello.c,放到cgi.c同一个索引上面。

$ gcc -o cgi_hello cgi_hello.c

选取C达成贰个cgi服务器,其实正是WEB服务器并顺便调用cgi程序作用。依据USportageL中的路线获取cgi程序名,并实行该cgi程序获得再次回到结果并重临给客户端。注意,是在WEB服务器程序中装置的情形变量,通过execl实行cgi程序,cgi程序因为是fork+exec施行的,子进程是会复制父进度处境变量表到温馨的进度空间的,所以能够读取意况变量QUERY_STRING。在浏览器输入
http://192.168.56.18:6006/cgi_hello?name=ssj(测试机ip为192.168.56.18)
能够见到重返 Hello: ssj

 

2.伊始化诉求对象

fcgi_request指标分配内部存款和储蓄器,绑定监听的 socket 套接字。

fcgi_init_request(&request, fcgi_fd);

一切央浼从输入到再次回到,都围绕着fcgi_request结构体对象在进行。

typedef struct _fcgi_request {
    int            listen_socket;
    int            fd;
    int            id;
    int            keep;
    int            closed;

    int            in_len;
    int            in_pad;

    fcgi_header   *out_hdr;
    unsigned char *out_pos;
    unsigned char  out_buf[1024*8];
    unsigned char  reserved[sizeof(fcgi_end_request_rec)];

    HashTable     *env;
} fcgi_request;

深入FastCGI协议

从效果上来说,CGI 合同已经完全能够缓和 Web 服务器与 Web
应用之间的多少通讯难题。然而出于每一个伏乞都亟需重新 fork 出 CGI
子进度导致质量堪忧,所以听他们说 CGI 左券的幼功上做了校订便有了 FastCGI
合同,它是生龙活虎种常驻型的 CGI 公约。

实质上来将 法斯特CGI 和 CGI 左券差没多少统统同样,它们都足以从 Web
服务器里采取到均等的多寡,区别的地方在于选用了分化的通讯形式。

再来回想一下 CGI 合同每回接到到 HTTP 诉求时,都急需经验 fork 出 CGI
子进度、试行处理并销毁 CGI 子进度这生龙活虎多级工作。

FastCGI 契约利用 进度间通讯
来管理客商的倡议,上面我们就来走访它的运营规律。

3.3 实现

全体上看来,WSGI服务器摆正是收到央求,设置好情形变量,然后调用应用对象管理须求。而选择对象调用start_response函数设置底部(注意,当时还从未回到响应给客商端),然后选拔对象回来一个可迭代对象(如Python的列表)给劳务器端。服务端对选取对象回来的迭代数据进行输出,输出前会先调用send_headers()来发送响应尾部。

后生可畏体化的示范代码参见
web-basis-wsgi,代码基本来自Python自带的wsgiref和http相关模块。

Apache加载的是PHP模块,那么这些模块时怎么贯彻的啊?Apache2的mod_php5模块包含sapi/apache2handler和sapi/apache2filter四个目录,在apache2_handle/mod_php5.c文件中,模块定义的连带代码如下:

7.停止伏乞

fcgi_finish_request(&request, 1);

int fcgi_finish_request(fcgi_request *req, int force_close)
{
    int ret = 1;

    if (req->fd >= 0) {
        if (!req->closed) {
            ret = fcgi_flush(req, 1);
            req->closed = 1;
        }
        fcgi_close(req, force_close, 1);
    }
    return ret;
}

fcgi_finish_request中调用fcgi_flushfcgi_flush中封装二个FCGI_END_REQUEST消息体,再通过safe_write写入
socket 连接的顾客端描述符。

何以是 法斯特CGI 而非 CGI 合同

若是单纯因为专门的学问形式的两样,就像并不曾什么样大不断的。并没到非要选用法斯特CGI 契约不可的境地。

然则,对于这么些犹如细小的出入,但意义优秀,最后的结果是促成出来的 Web
应用构造上的异样。

3.1 WSGI规范

WSGI是Web服务器网关接口(Python Web Server Gateway
Interface,缩写为WSGI)是为Python语言定义的WEB服务器和WEB应用程序或框架之间的风度翩翩种简易而通用的接口,它与CGI相通,它不是意气风发种框架,亦不是模块,而是生机勃勃种服务器(Web
Server卡塔尔国和应用程序(Web
Application卡塔尔(قطر‎之间规范。WSGI探讨实际上是概念了意气风发种WEB服务器与WEB框架解耦的专门的学业,开拓者能够筛选随机的WEB
服务器和WEB应用组合完结团结的web应用。比方常用的uWSGI和Gunicorn都以促成了WSGI
Server合同的服务器(uWSGI还兼有进程微电脑,监察和控制,日志,插件,网关等作用卡塔尔,Flask是兑现了WSGI
Application左券的行使框架(当然Flask也自带有三个简易的WEB服务器,即使大家日常是用nginx来拍卖静态文件卡塔尔(قطر‎,能够依靠项目意况搭配使用。

WSGI分为两端:服务器/网关端 和
应用/框架端,服务器端调用应用端提供的可调用的靶子。可调用对象能够是函数、方法、类照旧完毕了__call__艺术的实例,那有赖于服务器和接纳接收哪个种类完成手艺。除了尊重的服务器和平运动用,也得以行使中间件才干来实现该规范。

当服务端最早化完结后,过程调用accept函数步向拥塞状态,在main函数中大家见到如下代码:

FastCGI 消息头

如上,法斯特CGI
新闻分10种新闻类型,有的是输入过多输出。而持有的新闻都以三个音讯头开端。其组织体定义如下:

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标记法斯特CGI公约版本。
  • type 标志法斯特CGI记录类型,也便是记录奉行的雷同意义。
  • requestId标记记录所属的FastCGI央求。
  • contentLength记录的contentData组件的字节数。

有关地点的xxB1xxB0的协商表明:当四个相邻的构造组件除了后缀“B1”和“B0”之外命名相同一时候,它意味着那多少个构件可便是评估价值为B1<<8
+
B0的单个数字。该单个数字的名字是那么些构件减去后缀的名字。这几个约定归结了贰个由超越三个字节表示的数字的处理方式。

比方合同头中requestIdcontentLength表示的最大值正是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
}

你或然会想到借使一个新闻体长度超越65535如何做,则分割为多个一律类别的新闻发送就能够。

  • 介绍
  • 深入CGI协议
    • CGI的运作规律
    • CGI和睦的毛病
  • 深入FastCGI协议
    • 法斯特CGI公约运维规律
    • 为啥是 法斯特CGI 而非 CGI 协议
    • CGI 与 FastCGI 架构
    • 再看 FastCGI 协议
    • Web 服务器和 法斯特CGI 人机联作进程
    • 缘何需求在消息头发送 RequestID 那一个标记?
  • PHP-FPM

4.1 uWSGI安装配置

uWSGI是三个WEB服务器,它完毕了WSGI左券、uwsgi合计、http左券等。这里要区分下:uWSGI是WEB服务器,而小写的uwsgi是协商。安装uWSGI的步子比较容易,如下:

# sudo apt-get install build-essential python-dev
# sudo pip install uwsgi

下一场大家得以编制一个总结的契合WSGI标准的python程序:

# foobar.py
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

运行:

# uwsgi --http :9090 --wsgi-file foobar.py

那个时候,大家就足以在浏览器输入
http://127.0.0.1:9090来拜会了。内定参数--http则是以HTTP服务器格局运维,在实际上项目中,平日会以socket的章程运转,nginx担负管理静态财富,动态央求则由nginx通过uwsgi和煦与uWSGI服务器交互作用。

配置nginx如下:

# /etc/nginx/sites-enabled/uwsgi
server {
    listen 9090;
    location / {
        include uwsgi_params;
         uwsgi_pass 127.0.0.1:3031;
    }
}

以socket的方法运营uWSGI如下(加了经过和线程数配置State of Qatar:

uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2

为了便于,能够将起动参数放到配置文件 config.ini中,然后
uwsgi config.ini即可。

## config.ini示例
[uwsgi]
uid = nobody
gid = nogroup
socket = 127.0.0.1:3031
chdir = /home/vagrant/project/uwsgi
wsgi-file = foobar.py
processes = 4
threads = 2

万意气风发nginx里面配备的是proxy_pass http://127.0.0.1:3031,则那时候亟待将uwsgi以
http-socket的方法运维,即

uwsgi --http-socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 

  Step4:法斯特CGI子进度完毕管理后将行业内部输出和不当新词从同三回九转接再次来到Web
Server
当法斯特CGI子进度关闭连接时,央求便甘休。FastCGI子进程接着等待并管理来自法斯特CGI进度微处理机(运营在Web
Server中卡塔尔的下二个连连。在CGI格局中,php-cgi在那便脱离了。

筹划专门的学业

可能上面的开始和结果通晓起来依旧很肤浅,那是出于第生龙活虎对法斯特CGI合同还尚无三个光景的认知,第二一直不实际代码的读书。所以须要事情发生早前学习下
法斯特CGI
左券的剧情,不自然需求完全看懂,可大概领会之后,看完本篇再组成着读书通晓消化摄取。

http://www.fastcgi.com/devkit… (土耳其语原版)
http://andylin02.iteye.com/bl… (中文版)

目录

FastCGI程序发送给WEB服务器的多少包:

  • 先是个消息是 STDOUT
    。首个字节依旧01为version,第三个字节06表示项目为STDOUT,接着3-4字节0001依然requestId,5-6字节0061为新闻体长度97,7-8字节0700代表填充字段为7字节。接下来消息体便是回到的从头到尾的经过Status: 200\r\n...

  • 首个音信依旧
    STDOUT,不过是空的STDOUT音讯,用来标志STDOUT音讯结束。

  • 其四个音讯是
    END_REQUEST。第3个字节01依然version,第4个字节03标记类型
    END_REQUEST,3-4字节为requestId为1,5-6字节为音信体大小为8,7-8字节0000为填充字节长度。前边新闻体内容为8个0字节。约等于说appStatus为0,protocolStatus也为0.当中protocalStatus是合同级的状态码,为0表示
    REQUEST_COMPLETE,即央求平日完结。

// 消息类型定义
#define FCGI_BEGIN_REQUEST       1
#define FCGI_ABORT_REQUEST       2
#define FCGI_END_REQUEST         3
#define FCGI_PARAMS              4
#define FCGI_STDIN               5
#define FCGI_STDOUT              6
#define FCGI_STDERR              7
#define FCGI_DATA                8
#define FCGI_GET_VALUES          9
#define FCGI_GET_VALUES_RESULT  10
#define FCGI_UNKNOWN_TYPE       11
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)

地点的模块构造与大家在mod_php5.c中所看到的布局有几许不风姿罗曼蒂克,那是出于STANDAHavalD20_MODULE_STUFF的来头,这么些宏它包蕴了前面8个字段的概念。STANDALX570D20_MODULE_STUFF宏的概念如下:

PHP 中的 FastCGI 的实现

下直面代码的解读笔记只是自身个人文化的一个梳理提炼,如有核对,请大家建议。对不纯熟该代码的同班来讲或者是八个指导,最早认知,若是感觉很模糊不明晰,那么仍然要求和煦逐行去阅读。

php-src/sapi/cgi/cgi_main.c为例实行剖析表明,假如开辟景况为 unix
情况。main 函数中有些变量的概念,以至 sapi
的开首化,大家就不商量在那商讨了,只表达有关 法斯特CGI 相关的内容。

介绍

在用PHP开垦的进度中,我们平常使用Nginx大概Apache作为大家的Web服务器。可是PHP是怎么与这个Web服务器通讯的吧?

  • Apache把PHP作为叁个模块集成到Apache进程运维,这种mod_php的运行形式与PHP-CGI未有任何关系。

  • Nginx是通过FastCGI来贯彻与PHP的通讯。

要谈FastCGI就必须要先说说CGI。那怎么是CGI?

CGI(Common Gateway Interface:通用网关接口卡塔尔(قطر‎是Web
服务器运营时外界程序的科班,按CGI 编写的顺序能够扩大服务器成效。CGI
应用程序能与浏览器进行相互作用,还可通过数据库API
与数据库服务器等外界数据源举行通信,从数据库服务器中获取数据。–百度百科

CGI协议同 HTTP 合同同样是一个「应用层」左券,它的 成效 是为精晓决 Web
服务器与 PHP 应用(或任何 Web 应用)之间的通信难点。

既然如此它是贰个「左券」,换言之它与语言非亲非故,即假诺是促成类 CGI
合同的运用就能够完毕相互作用的通讯。

1 CGI

  Step2:法斯特CGI进程微处理器自己带头化,运营多少个CGI解释器进程(可以看到多个php-cgi卡塔尔并等候来自web
server的连接

观念 CGI 工作原理分析

顾客端访谈某些 U奥迪Q7L 地址然后,通过 GET/POST/PUT 等艺术交给数据,并透过
HTTP 合同向 Web 服务器发出哀告,服务器端的 HTTP Daemon(守护进度)将
HTTP 诉求里描述的信息透过标准输入 stdin 和情形变量(environment
variable卡塔尔传递给主页钦命的 CGI
程序,并运转此应用程序实行管理(满含对数据库的拍卖),管理结果通过规范输出
stdout 重返给 HTTP Daemon 守护进程,再由 HTTP Daemon 进度经过 HTTP
公约重回给客户端。

地点的这段话领悟大概照旧比较空虚,上面大家就透过一遍GET诉求为例举办详尽表明。

澳门大赌坊 2

上边用代码来兑现图中表述的效用。Web 服务器运维多少个 socket
监听服务,然后在地头试行 CGI 程序。前边有比较详细的代码解读。

缘何必要在音信头发送 RequestID 那个标志?

倘使是各样连接仅管理贰个央浼,发送 RequestID 则略显多余。

而是大家的 Web 服务器和 法斯特CGI
进程之间的连接可能管理多少个央浼,即叁个连连能够拍卖多少个央浼。所以才须要利用数据中国包装技左券而不是直接运用单个数据流的原故:以完结「多路复用」。

由此,由于每一种数据包都饱含唯意气风发的 RequestID,所以 Web
服务器才具在三个老是上发送放肆数量的呼吁,何况 法斯特CGI
进程也可以从一个连接上接纳到任性数量的央浼数据包。

除此以外大家还亟需明显一点便是 Web 服务器 与 法斯特CGI 进程间通讯是
冬天的。就算大家在相互进度中看起来二个呼吁是安如泰山的,不过大家的 Web
服务器也许有十分大只怕在相同的时候发出几11个 BEGIN_REQUEST
类型的数据包,由此及彼。

4.2 uwsgi协议

前方大家深入分析过fastcgi和wsgi协议,而uwsgi是uWSGI独有的用来与WEB服务器通讯的争论,它是叁个字节公约,可以传输大肆数据类型,nginx也已经扶助uwsgi磋商,如我辈眼下用到的uwsgi_pass。

uwsgi磋商的商丘如下:共三十九个人,前边8位为标记,中间21位是数据包大小,最终8位也为标记。作者这里深入分析的是modifier1为0的情形,即数据包为WSGI变量,datasize为WSGI块变量大小(不包蕴央浼体卡塔尔(قطر‎。其余选用详细含义能够参见
uwsgi-protocol.

struct uwsgi_packet_header {
    uint8_t modifier1;
    uint16_t datasize;
    uint8_t modifier2;
};

struct uwsgi_var {
    uint16_t key_size;
    uint8_t key[key_size];
    uint16_t val_size;
    uint8_t val[val_size];
}

实例解析:

早先边例子表明,大家应用tcpdump命令查看nginx和uWSGI之间的通讯包

tcpdump -i lo port 3031 -n -X -vvvv

运行 curl -i http://127.0.0.1:9090
能够看来如下输出:

 127.0.0.1.36705 > 127.0.0.1.3031
    0x0000:  4500 0193 9511 4000 4006 a651 7f00 0001  E.....@.@..Q....
    0x0010:  7f00 0001 8f61 0bd7 441d 5d4e c7f6 50e7  .....a..D.]N..P.
    0x0020:  8018 02ab ff87 0000 0101 080a 0009 c842  ...............B
    0x0030:  0009 c842 005b 0100 0c00 5155 4552 595f  ...B.[....QUERY_
    0x0040:  5354 5249 4e47 0000 0e00 5245 5155 4553  STRING....REQUES
    0x0050:  545f 4d45 5448 4f44 0300 4745 540c 0043  T_METHOD..GET..C
    ...

127.0.0.1.3031 > 127.0.0.1.36705
   ...
    0x0030:  0009 c842 4854 5450 2f31 2e31 2032 3030  ...BHTTP/1.1.200
    0x0040:  204f 4b0d 0a43 6f6e 7465 6e74 2d54 7970  .OK..Content-Typ
    0x0050:  653a 2074 6578 742f 6874 6d6c 0d0a 0d0a  e:.text/html....
    0x0060:  4865 6c6c 6f20 576f 726c 64              Hello.World

除去前面ip和tcp遵义,能够看出实际内容从
005b 0100开班,即uwsgi包的长短为
0x015b共347字节,前面则是实际上内容,如QUERY_STRING本条变量key长度为12(0c00卡塔尔,前边紧跟key,而value长度为0(0000State of Qatar,未有内容。前面REQUEST_METHOD的key长度为14(0e00卡塔尔国,value为GET,长度为3(0300),就那样推算,跟fastcgi左券有一点相像,这么些变量来自/etc/nginx/uwsgi_params中定义和nginx增多的HTTP_USER_AGENT等HTTP变量。uWSGI发送给nginx的响应报文则是正式的HTTP响应。

 

写在结尾

把 法斯特CGI
的学识学习领悟的长河做了那般豆蔻梢头篇笔记,把本人明白的从头到尾的经过(自己觉得)有系统地写出来,能够让别人比较简单看领会也是意气风发件不挺不便于的事。同一时间也让协和对这一个知识点的明白又深刻了黄金年代层。对
PHP 代码学习驾驭中还会有众多思疑的地点还亟需本身本身中期渐渐消食和透亮。

本文都以戮力一心的有个别明亮,水平有限,如有更改,希望大家给与指正。

咬牙看完本的都以老驾车员,说真话,前边有个别太单调了!假使能把种种知识点真正通晓消食,相对收获颇丰。

4.3 uWSGI与使用框架组合使用

uWSGI能够与Flask,Django,web2py等框架组合使用,在大家的其实项目中,布局平日是nginx + uWSGI + Flask,即静态须要由nginx管理,动态诉求转载到uWSGI,然后组合使用Flask框架来编排专门的学业逻辑,当然里面普通还有只怕会用到gevent等。

uWSGI与Flask组合使用也很容易。因为Flask框架将WSGI的可调用利用对象暴表露来了,大家倘若在开发银行参数中指明入口的app就能够。先安装flask模块:

# sudo pip install flask

下一场编写flask程序如下:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return "I am app 1"

下一场运维uWSGI(比早先只是多三个 --callable 参数):

uwsgi --socket 127.0.0.1:3031 --wsgi-file flaskapp.py --callable app --processes 4 --threads 2

4、服务器进度调用accept函数踏入拥塞状态,直到有客商进程调用connect函数而树立起壹个三回九转;

发表评论

电子邮件地址不会被公开。 必填项已用*标注