3.7.2 将内存中的字符串作为包体发送
调用ngx_http_output_filter方法即可向客户端发送HTTP响应包体,下面查看一下此方法的原型,如下所示。
- ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in);
ngx_http_output_filter的返回值在mytest例子中不需要处理,通过在ngx_http_mytest_handler方法中返回的方式传递给ngx_http_finalize_request即可。ngx_chain_t结构已经在3.2.6节中介绍过,它仅用于容纳ngx_buf_t缓冲区,所以需要先了解一下如何使用ngx_buf_t分配内存。下面介绍Nginx的内存池是如何分配内存的。
为了减少内存碎片的数量,并通过统一管理来减少代码中出现内存泄漏的可能性,Nginx设计了ngx_pool_t内存池数据结构。本章我们不会深入分析内存池的实现,只关注内存池的用法。在ngx_http_mytest_handler处理方法传来的ngx_http_request_t对象中就有这个请求的内存池管理对象,我们对内存池的操作都可以基于它来进行,这样,在这个请求结束的时候,内存池分配的内存也都会被释放。
- struct ngx_http_request_s {
- …
- ngx_pool_t *pool;
- …
- };
实际上,在r中可以获得许多内存池对象,这些内存池的大小、意义及生存期各不相同。第3部分会涉及许多内存池,本章使用r->pool内存池即可。有了ngx_pool_t对象后,可以从内存池中分配内存。例如,下面这个基本的申请分配内存的方法:
- void *ngx_palloc(ngx_pool_t *pool, size_t size);
其中,ngx_palloc函数将会从pool内存池中分配到size字节的内存,并返回这段内存的起始地址。如果返回NULL空指针,则表示分配失败。还有一个封装了ngx_palloc的函数ngx_pcalloc,它多做了一件事,就是把ngx_palloc申请到的内存块全部置为0,虽然,多数情况下更适合用ngx_pcalloc来分配内存。
假如要分配一个ngx_buf_t结构,可以这样做:
- ngx_buf_t* b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
这样,ngx_buf_t中的成员指向的内存仍然可以继续分配,例如:
- b->start = (u_char*)ngx_pcalloc(r->pool, 128);
- b->pos = b->start;
- b->last = b->start;
- b->end = b->last + 128;
- b->temporary = 1;
实际上,Nginx还封装了一个生成ngx_buf_t的简便方法,它完全等价于上面的6行语句,如下所示。
- ngx_buf_t *b = ngx_create_temp_buf(r->pool, 128);
分配完内存后,可以向这段内存写入数据。当写完数据后,要让b->last指针指向数据的末尾,如果b->last与b->pos相等,那么HTTP框架是不会发送一个字节的包体的。
最后,把上面的ngx_buf_t *b用ngx_chain_t传给ngx_http_output_filter方法就可以发送HTTP响应的包体内容了。例如:
- ngx_chain_t out;
- out.buf = b;
- out.next = NULL;
- return ngx_http_output_filter(r, &out);
注意 在向用户发送响应包体时,必须牢记Nginx是全异步的服务器,也就是说,不可以在进程的栈里分配内存并将其作为包体发送。当ngx_http_output_filter方法返回时,可能由于TCP连接上的缓冲区还不可写,所以导致ngx_buf_t缓冲区指向的内存还没有发送,可这时方法返回已把控制权交给Nginx了,又会导致栈里的内存被释放,最后就会造成内存越界错误。因此,在发送响应包体时,尽量将ngx_buf_t中的pos指针指向从内存池里分配的内存。
参考:http://book.51cto.com/art/201303/386675.htm