3.8.2 清理文件句柄

Nginx会异步地将整个文件高效地发送给用户,但是我们必须要求HTTP框架在响应发送完毕后关闭已经打开的文件句柄,否则将会出现句柄泄露问题。设置清理文件句柄也很简单,只需要定义一个ngx_pool_cleanup_t结构体(这是最简单的方法,HTTP框架还提供了其他方式,在请求结束时回调各个HTTP模块的cleanup方法,将在第11章介绍),将我们刚得到的文件句柄等信息赋给它,并将Nginx提供的ngx_pool_cleanup_file函数设置到它的handler回调方法中即可。首先介绍一下ngx_pool_cleanup_t结构体。


typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;

struct ngx_pool_cleanup_s{

//执行实际清理资源工作的回调方法

ngx_pool_cleanup_pt handler;

//handler回调方法需要的参数

void*data;

//下一个ngx_pool_cleanup_t清理对象,如果没有,需置为NULL

ngx_pool_cleanup_t*next;

};


设置好handler和data成员就有可能要求HTTP框架在请求结束前传入data成员回调handler方法。接着,介绍一下专用于关闭文件句柄的ngx_pool_cleanup_file方法。


void ngx_pool_cleanup_file(void*data)

{

ngx_pool_cleanup_file_t*c=data;

ngx_log_debug1(NGX_LOG_DEBUG_ALLOC,c->log,0,"file cleanup:fd:%d",c->fd);

if(ngx_close_file(c->fd)==NGX_FILE_ERROR){

ngx_log_error(NGX_LOG_ALERT,c->log,ngx_errno,

ngx_close_file_n"\"%s\"failed",c->name);

}

}


ngx_pool_cleanup_file的作用是把文件句柄关闭。从上面的实现中可以看出,ngx_pool_cleanup_file方法需要一个ngx_pool_cleanup_file_t类型的参数,那么,如何提供这个参数呢?在ngx_pool_cleanup_t结构体的data成员上赋值即可。下面介绍一下ngx_pool_cleanup_file_t的结构。


typedef struct{

//文件句柄

ngx_fd_t fd;

//文件名称

u_char*name;

//日志对象

ngx_log_t*log;

}ngx_pool_cleanup_file_t;


可以看到,ngx_pool_cleanup_file_t中的对象在ngx_buf_t缓冲区的file结构体中都出现过了,意义也是相同的。对于file结构体,我们在内存池中已经为它分配过内存,只有在请求结束时才会释放,因此,这里简单地引用file里的成员即可。清理文件句柄的完整代码如下。


ngx_pool_cleanup_t*cln=ngx_pool_cleanup_add(r->pool,sizeof(ngx_pool_cleanup_file_t));

if(cln==NULL){

return NGX_ERROR;

}

cln->handler=ngx_pool_cleanup_file;

ngx_pool_cleanup_file_t*clnf=cln->data;

clnf->fd=b->file->fd;

clnf->name=b->file->name.data;

clnf->log=r->pool->log;


ngx_pool_cleanup_add用于告诉HTTP框架,在请求结束时调用cln的handler方法清理资源。

至此,HTTP模块已经可以向客户端发送文件了。下面介绍一下如何支持多线程下载与断点续传。