ngx_event_pipe_write_to_downstream分析

概述

        这篇博客中我们重点阐述nginx http proxy模块如何向客户端发送响应。这也是一个比较复杂的过程,我们在叙述的过程中省略去一些过滤模块的钩子函数,直接进入发送响应数据的处理逻辑,http proxy模块触发发送响应的函数是ngx_event_pipe_write_to_downstream,这篇博客就对这个函数作一些简单分析。

实现

        总的说来,ngx_event_pipe_write_to_downstream实现中主要有三个过程:1.准备写出的缓冲区;2. 将缓冲区发送给客户端;3.更新本地缓存状态。我们分别从这三点来分析一次完整的数据发送过程。
  • 准备写出缓冲区
        之前的博客中我们说到了nginx会将不同状态的缓冲区链入不同链表上。与发送相关的缓冲区链表有三:busy链表,out链表,in链表。后面的两个链表我们前面已经做过分析,busy链表存储的是上次发送过程中没有写出的部分。
        因此,我们发送的顺序应该是:busy链表,out链表,in链表(必须要按照响应body的偏移顺序发送)。另外,每次发送多少个缓冲区也是可以设置的,nginx可以通过proxy_busy_bufs来设置每次要发送的响应大小。因此,整个的准备缓冲区代码如下所示:
        for (cl = p->busy; cl; cl = cl->next) {

            if (cl->buf->recycled) {
		// ʲôÇé¿öÏ»á³öÏÖÇ°Ò»¸ö»º³åÇøºÍºóÒ»¸ö»º³åÇø
		// µÄÆðʼµØÖ·Ïàͬ?
                if (prev == cl->buf->start) {
                    continue;
                }

                bsize += cl->buf->end - cl->buf->start;
                prev = cl->buf->start;
            }
        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
                       "pipe write busy: %uz", bsize);

        out = NULL;
	// 如果busy buffers链表上的缓冲区数量已经足够,那么无需再从out和in链表上去取了,直接flush
        if (bsize >= (size_t) p->busy_size) {
            flush = 1;
            goto flush;
        }

        flush = 0;
        ll = NULL;
        prev_last_shadow = 1;

        for ( ;; ) {
            if (p->out) { // 优先发送out链表,因为这部分响应偏移在前
                cl = p->out;
                if (cl->buf->recycled) {
                    ngx_log_error(NGX_LOG_ALERT, p->log, 0,
                                  "recycled buffer in pipe out chain");
                }

                p->out = p->out->next;

            } else if (!p->cacheable && p->in) {
                cl = p->in;

                ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,
                               "pipe write buf ls:%d %p %z",
                               cl->buf->last_shadow,
                               cl->buf->pos,
                               cl->buf->last - cl->buf->pos);

                if (cl->buf->recycled && prev_last_shadow) {
                    if (bsize + cl->buf->end - cl->buf->start > p->busy_size) {
                        flush = 1;
                        break;
                    }

                    bsize += cl->buf->end - cl->buf->start;
                }

                prev_last_shadow = cl->buf->last_shadow;

                p->in = p->in->next;

            } else {
                break;
            }

            cl->next = NULL;

            if (out) {
                *ll = cl;
            } else {
                out = cl;
            }
            ll = &cl->next;
        }
  • 发送响应
        发送响应的过程略显简单,直接调用了如下函数
        // 最终调用ngx_http_write_filter()将数据发送出去
        rc = p->output_filter(p->output_ctx, out);
        只是我们需要了解的一点是,这个发送过程可能并没有将所有的准备好的响应数据发送出去,可能只发送了部分而已,另外发送过程中可能由于种种原因出错,在发送函数中会设置种种的错误码。
  • 更新本地缓存状态
        发送完成后需要更新本地缓存状态。如busy buffer链表上的缓冲区可以释放了、本次未发送成功的buffer需要添加到busy buffer链表上 ......,这个过程会在下面的代码中实现:
        // 将已经发送出去的缓冲区释放,添加到free链表
	// 将尚未发送出去的缓冲区添加至busy链表,下次优先发送
        ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag);
另外,对于发送出错,还得设置错误码:
  if (rc == NGX_ERROR) {
            p->downstream_error = 1;
            return ngx_event_pipe_drain_chains(p);
        }
由于上面刚刚将发送完成的缓冲区添加到了free链表,注意:这里只是将管理缓冲区的数据结构释放到free链表,真正的原始缓冲区可能尚未释放(插入到free_raw_bufs链表)。因此,接下来我们得处理这种情况,需要遍历free链表上的所有buffer,如果其原始的数据缓冲区尚未释放,那么free it。
for (cl = p->free; cl; cl = cl->next) {

            if (cl->buf->temp_file) {
                if (p->cacheable || !p->cyclic_temp_file) {
                    continue;
                }

                /* reset p->temp_offset if all bufs had been sent */

                if (cl->buf->file_last == p->temp_file->offset) {
                    p->temp_file->offset = 0;
                }
            }

            /* TODO: free buf if p->free_bufs && upstream done */

            /* add the free shadow raw buf to p->free_raw_bufs */

            if (cl->buf->last_shadow) {
                if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {
                    return NGX_ABORT;
                }

                cl->buf->last_shadow = 0;
            }

            cl->buf->shadow = NULL;
        }
    }





你可能感兴趣的:(nginx,HTTP_proxy)