nginx subrequest的实现解析

本文探讨了nginx中的子请求(subrequest)概念,它在处理HTTP请求时提供便利但可能影响效率。通过使用ngx_http_post_subrequest_t回调和upstream模块,可以异步获取数据。nginx通过一种树和链表结合的结构实现subrequest,其中postponed链表用于管理请求顺序。文章详细解析了创建subrequest的源码,解释了数据发送顺序的管理,以及在ngx_http_run_posted_requests函数中的遍历过程。
摘要由CSDN通过智能技术生成

        大家都知道nginx里面有一个subrequest的概念,也就是子请求,它并不是http标准里面的概念,它是在当前请求中发起的一个新的请求,它拥有自己的ngx_http_request_t结构,uri和args。一般来说使用subrequest的效率可能会有些影响,因为它需要重新从server rewrite开始走一遍request处理的PHASE,但是它在某些情况下使用能给我们带来方便,现在我们比较常用的是用subrequest来访问一个upstream的后端,并给它一个ngx_http_post_subrequest_t的回调handler,这样有点类似于一个异步的函数调用。对于从upstream返回的数据,subrequest允许根据创建时指定的flag,来决定由用户自己处理(回调handler中)还是由upstream模块直接发送到out put filter。简单的说一下subrequest的行为,nginx使用subrequest访问某个location,产生相应的数据,并插入到nginx输出链的相应位置(创建subrequest时的位置),下面我用agentzh(章亦春,之前是公司北京同事,最近离职了,据说回家专心搞开源)的echo模块(https://github.com/agentzh/echo-nginx-module)来举例说明一下:

location /main {
    echo nginx;
    echo_location /sub;
    echo world;
}
location /sub {
    echo hello;
}
访问/main,将得到如下响应:

nginx
hello
world
       上面的echo_location指令是发起一个subrequest来访问/sub,echo指定类似shell里面里面的echo,用来输出其后的字符串,顺便说一下echo模块还有其他很多的指令,这个模块在测试的时候非常有用。

       在进行源码解析之前,先来想想如果是我们自己要实现subrequest的上述行为,该如何来做?subrequest还可能有自己的subrequest,而且每个subrequest输出数据都不一定是按照其创建的顺序来的,所以这里简单的采用链表来做是不好实现的,于是我们进一步联想到可以采用树的结构来做,主请求即为根节点,每个节点可以有自己的子节点,遍历某节点表示处理某请求,自然的可以想到这里可能是用后根(序)遍历的方法,没错,实际上Igor采用树和链表结合的方式实现了subrequest的功能,但是由于节点(请求)产生数据的顺序不是固定按节点创建顺序(左->右),而且可能分多次产生数据,不能简单的用后根(序)遍历。Igor使用了2个链表的结构来实现,第一个是每个请求都有的postponed链表,一般情况下每个链表节点保存了该请求的一个子请求,该链表节点定义如下:

struct ngx_http_postponed_request_s {
    ngx_http_request_t               *request;
    ngx_chain_t                      *out;
    ngx_http_postponed_request_t     *next;
};
        可以看到它有一个request字段,可以用来保存子请求,另外还有一个ngx_chain_t类型的out字段,实际上一个请求的postponed链表里面除了保存子请求的节点,还有保存该请求自己产生的数据的节点,数据保存在out字段;第二个是posted_requests链表,它挂载了当前需要遍历的请求(节点), 该链表保存在主请求(根节点)的posted_requests字段,链表节点定义如下:

struct ngx_http_posted_request_s {
    ngx_http_request_t               *request;
    ngx_http_posted_request_t        *next;
};

        在ngx_http_run_posted_requests函数中会顺序的遍历主请求的posted_requests链表:

void
ngx_http_run_posted_requests(ngx_connection_t *c)
{
    ...
    for ( ;; ) {
        /* 连接已经断开,直接返回 */
        if (c->destroyed) {
            return;
        }

        r = c->data;
        /* 从posted_requests链表的队头开始遍历 */
        pr =
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值