nginx写临时文件过程分析

来源:互联网 发布:最新微信家校通源码 编辑:程序博客网 时间:2024/05/29 12:55

综述

        Nginx作为反向代理服务器时,从后端服务器接收响应数据缓存在本地并发送给客户端。既然缓存在本地就要使用内存,如果一个请求的响应特别大,且可能nginx到客户端的发送速度比较慢就可能会导致请求全部缓存在内存中,导致内存使用偏高。nginx可以通过指令设置每个请求的响应body使用的缓冲区大小和数量。当出现缓冲区不足时就要将部分缓冲区的数据写入临时文件中,以释放部分缓冲区可再次缓存数据。

触发

        在ngx_event_pipe_read_upstream()从上游接收数据时首先得准备缓冲区以存放数据。此时有可能出现空闲缓冲区不足的情况,因此需要将部分内存响应数据写入临时文件以释放这些缓冲区,如下:
            // 迫不得已时才写临时文件            } else if (p->cacheable                       || p->temp_file->offset < p->max_temp_file_size)            {                /*                 * if it is allowed, then save some bufs from r->in                 * to a temporary file, and add them to a r->out chain                 */                rc = ngx_event_pipe_write_chain_to_temp_file(p);                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,                               "pipe temp offset: %O", p->temp_file->offset);                if (rc == NGX_BUSY) {                    break;                }   ......

执行过程

        这个执行过程也很简单,主要分为三大步骤:1. 准备写入缓冲区;2.写入;3.更新状态并释放已写入的缓冲区。我们分别来阐述。
  • 准备写入缓冲区
             我们可按照最简单的想法来理解:即一次选择部分in链表上的缓冲区写入,这里的部分是可以通过nginx指令配置的,即每次写入的字节数temp_file_write_size。选择好这些缓冲区就可以将它们从in链表上摘除了。
  • 写入
             一旦准备好了数据,我们就可以写入了,写入的过程非常简单,就是调用操作系统接口写文件而已,当然这里还有些tricks,但我们并不太关心,调用的是函数ngx_write_chain_to_temp_file
  • 更新状态
             这个过程比较有意思。因为nginx会维护一些响应状态,如响应是在内存中还是在临时文件中,在临时文件中的起始位置等等信息。后面的过滤、发送响应等过程都会依
赖这些状态信息,因此,这个非常重要。还记得我在前一篇博客中描述的out链表么,这个链表上面存储的就是每段位于临时文件中的响应的状态信息,如起始、结束 偏移等等。nginx会将这些信息抽象存储在ngx_buf_t数据结构中,因此这里需要更新原有的ngx_buf_t结构或者创建新的结构。
            为什么会有更新原有的ngx_buf_t结构和创建新的ngx_buf_t结构这两种情况呢?这是因为每个ngx_buf_t管理的是一段响应的状态,如果本次写入的那段响应正好和最近一次写入的响应在偏移上是连续,那么我们只需更新现有的ngx_buf_t数据结构;否则,我们就需要创建并初始化新的ngx_buf_t结构了,然后再将该结构插入到out链表的尾部。
  • 释放已写入缓冲区
             经过上面的步骤之后我们就可以释放已经写入临时文件的缓冲区了。这里也需要注意一个问题:nginx中存在所谓的"影子缓冲区"概念,即有些缓冲区管理结构可能会和其他的管理结构共享原始缓冲区,因此我们在释放的时候需要判断,如果是影子缓冲区,我们还必须得释放掉原始缓冲区(存放数据的缓冲区)。ngx_buf_t释放到free链表,而原始数据缓冲区则插入至free_raw_bufs链表的尾部。如下:
 for (cl = out; cl; cl = next) {        next = cl->next;        cl->next = p->free;        p->free = cl;        b = cl->buf;// 如果是其他缓冲区的影子缓冲区// 那么我们还得找到原始缓冲区并将原始缓冲区// 归还给free_raw_bufs        if (b->last_shadow) {            tl = ngx_alloc_chain_link(p->pool);            if (tl == NULL) {                return NGX_ABORT;            }            tl->buf = b->shadow;            tl->next = NULL;            *last_free = tl;            last_free = &tl->next;            b->shadow->pos = b->shadow->start;            b->shadow->last = b->shadow->start;            ngx_event_pipe_remove_shadow_links(b->shadow);        }    }
0 0
原创粉丝点击