• 貂蝉大乐透定胆
  •  首页  大乐透杀号  貂蝉大乐透杀号定胆  貂蝉大乐透杀号预测
    当前位置: > 貂蝉大乐透定胆 >

    解读 nginx 中 chain 跟 buf (1)

    时间:2017-07-10 13:42来源:未知 作者:admin 点击:
    未修改完,拒绝转载。 在写 nginx 的 filter 模块时候需要比较深刻去懂得chain 跟 buf的一些细节以及数据流处理进程。 就结构而言,这两个构造不算庞杂 struct ngx_chain_s { ngx_buf_t *buf; ngx_chain_t *next; }; struct ngx_buf_s { u_char *pos; u_char

    未修改完,拒绝转载。

    在写 nginx 的 filter 模块时候需要比较深刻去懂得chain 跟 buf的一些细节以及数据流处理进程。

    就结构而言,这两个构造不算庞杂

    struct ngx_chain_s {
        ngx_buf_t    *buf;
        ngx_chain_t  *next;
    };

    struct ngx_buf_s {
        u_char          *pos;
        u_char          *last;
        off_t            file_pos;
        off_t            file_last;

        u_char      ,大乐透杀号;    *start;         /* start of buffer */
        u_char          *end;           /* end of buffer */
        ngx_buf_tag_t    tag;
        ngx_file_t      *file;
        ngx_buf_t ,大乐透杀号;      *shadow;


        /* the buf's content could be changed */
        unsigned         temporary:1;

        /*
         * the buf's content is in a memory cache or in a read only memory
         * and must not be changed
         */
        unsigned         memory:1;

        /* the buf's content is mmap()ed and must not be changed */
        unsigned         mmap:1;

        unsigned         recycled:1;
        unsigned    ,大乐透杀号;     in_file:1;
        unsigned         flush:1;
        unsigned         sync:1;
        unsigned         last_buf:1;
        unsigned         last_in_chain:1;

        unsigned         last_shadow:1;
        unsigned         temp_file:1;

        /* STUB */ int   num;
    };

     

    很显明可以看出,chain是起到一个链条的作用,把要往外发送到数据串起来,buf是一个缓冲区块治理的结构,里面记载了这个缓冲区的起始、停止的地位,已经发送的数据的游标,还包含了一些标志位,这些标记位很主要。

    至此,咱们脑海里面大抵能够有一个这样的印象,每个chain下面挂接一个buf,而chain是用next这个成员指针一个接一个连起来,nginx内部会将一一chain传给 filter 模块处理,filter 模块会有多个,每个filter 模块处理完了就会把处置好的chan传给下一个filter,这个在良多材料里面都有提过了,这里侧重探讨的是这样设计目标是什么,在写一个filter 模块的时候有什么难点须要斟酌。

    实在,我们可以把chain看成是诸如视频中的“帧”的概念,我们知道处理视频数据流个别都会按“帧”来处理,然而帧的大小普通是固定的,而且帧与帧之间是绝对独破的。可是作为http数据流,chain与chain之间往往会产生比较多的关系。

    举个背面的简单例子:chunked_filter ,这个filter就需要在chain之间插入chunk的信息,这个问题还好办,在chain之间插入新chain就可以了。

    可是多数情形下是不那么简略的,再看一些文本调换的模块如sub_filter需要面对什么问题,我们知道假设有一个字符串 aabbccddeef f,如果我们要匹配替代bbcc的话当字符串放在是一个线性内存的空间的话是很轻易的事件,但是如果把字符串分成两段,aabb ccddeeff 分辨放在两个不同的chain的时候,问题就变得稍复杂了,如果是本人设计状况机去匹配,那就是先发现bb,然后记住这个状态,再去下一个chain去尝试查找cc,这是一种方法;如果是用正则去匹配,就得先把两个chain的内容先做合并,再做匹配,在这里如何处理好两个,两个以上的chain之间的数据就需要做额定多的工作了。

    要知道,在体系运行的过程中,完整的http正文并不是无时无刻、任何情况下放在chain链上供我们的filter模块访问的,在filter中chain链的机制只是方便我们去增补我们数据,而不是供给一个完全的http正文给我们随便操作的。这个理由我想很简单,如果所有要发送到内容都在chain链上了,那么要做什么修改当然都很方便,但是占用内存就非常可观了,我们可以推算一下,我们都知道nginx可以轻松接入10k衔接,乘一下每请求的数据大小就不难得同一时刻要耗费多少内存了。再一个很重要的一点就是,如果数据不在本地而是从代理端过来的话,如果想用户尽早接收到数据,也应当是一边接受一边发送,而不是全体吸收完了再发送给用户。

    事实上,多数的filter在实现上都是在原有的chain前后放入自己的chain就能满意需要了,例如上面提到的 chunked,sub,还有ssi 的filter实现都这样做,之前网络上面的入门教程也是给出例子的,这样做利益是逻辑简单,足够便利,只有chain链不搞乱,程序基础不会出大问题,但是,如果我们想在filter里面做更多的事情,这样显然这些例子是不能知足我们的需要。

     

    我认为:在http数据流之中不加限度的的插入chain或者把一个chain拆成多少个chain以到达filter的作用是不适当的,这里有首先有两个考虑:

    1、每级filter在处理的时候都需要遍历一次传入的chain链,说到这里,很有必要分享一个数据默认配置下经由我测试,一个1M左右文件,如果nginx是读取本地文件的话会产生大概30多个chain,假如是反向代办拜访的话,会有200多个chain,我们试想下,在反向署理的恳求中如果一个 filter 做了2、3次chain分裂那将会有濒临上千个chain产生,这个数目是比拟客观的,要晓得,其余 filter 也会发生chain决裂的,当然了,我们也可以写一个filter 来合并chain。

    2、正常情况下,在发送数据的时候unix如果用 write 或 sendfile 每次只能发送一块数据,如果chain很多的话,例如达到了 1000 以上,这个就比较影响效率的了,最坏情况下,1000个chain要调用1000次syscall才干发送出去,这个消费不能被忽视的;当然了,nginx这里是有一些优化的,在发送的模块里面内部会把内存相邻的两个chain并成一个内存块来发送,而且经过察看,其实nginx是调用writev 这个syscall来发送数据的,这样可以减少很多进出内核的次数。

    我信任,既然会搞出1k个chain那肯定有场景会搞出10k个chain,对于一个接入10k连接的Server来说,由chain链的造成的代价更是不容疏忽的,我们不要忘却,我们是如何对 select() 这个接口在c10k下的效力耿耿于怀的,由于这里同样也是要做大批的遍历。

    剖析到这里,我想根本上应该可以有一个论断:一个请求的性命周期中chain的数量要适度把持,filter在处理的数据的时候要尽量防止分裂出新的chain插入chain链中,如果能在原有的buf上面修改数据是上策,但是这个幻想的情况我认为也是有点不大事实的,因为 chain下的 buf 大小是被认为是不可变长的,你可以把其中内容经过filter占用的内存空间小了,这个可以,但是,要是经过filter处理后变大怎么办?那就只能把chain下的 buf 换掉了,或者把这个chain drop掉,换一个新chain上来。

    换chain或者换buf这个措施看起来在较多的情景下都能实用,这里有不少问题需要留神

    首先,以sub的模块为例:

    static ngx_int_t ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in);

    如接口所示,在 filter 中我们只能得到一个chain,并且可以在这个chain前面或者后面插入chain,但是当前请求的chain链是在别的地方,当前模块用畸形道路是访问不到的,不要指望 ngx_http_request_t *r 里面有可以访问当前请求chain链的指针,我看过了,没有。因此我们不能指望通过修正当前的chain链去摘掉一个chain及下面的buf。

    但是,如果打算对这个chain束之高阁,直接往下级传入一个新chain,会有两个问题:

    1、在上层调用的处所会以为这个本来的chain没发送出去,而后全部要求就挂逝世在这里。

    2、chain的内存不被及时开释,这样会造成内存沉积。

    对第二个问题,我们可以验证下,可以写一个空的 filter ,然后把每次传入的chain的buf的pos成员地址打印出来看看,当然这个条件是这个http的注释body要足够大,使得nginx会分拆成若干个chain来处理,我们会发现pos的地址多数情况下都是统一个地址,因而我们可以知道chain跟buf确定是被复用了,当一个chain被发送出去之后,就会被认为是空chain再度被投入轮回应用,就像一个一个水桶接力运水一样。用猪又的话来说,这种流式的设计是很省内存的。

    讨论到这里,我们就会发明要实现一个优雅的filter 这也不行,那也不行,确切如斯,这里是需要花许多心理去考虑周详,在各种利弊之间获得一个均衡。

    后面有时光再分享下,策略上,我们有哪些好思路。

     待续,未完。

    (责任编辑:admin)
    相关内容:
    大乐透杀号 一直与您同行