【Nginx】磁盘文件写入飞地发

【Nginx】磁盘文件写入飞地发

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

文章继续。什么时候Nginx当用户请求一个文件,这将无法读取该文件的内容加载到内存,然后从内存发送,但电话sendfile况下,从内核直接发送出去。这样做显然效率要更高。Nginx也为我们封装好了一系列的接口。以下就来说明怎样发送一个磁盘文件给client。


和从内存直接发送数据最大的不同在于ngx_buf_t缓冲区的设置方法。ngx_buf_t结构体的定义例如以下:
struct ngx_buf_s {
    ....
    off_t            file_pos;     // 文件起始位置
    off_t            file_last;    // 文件结束位置
    ....
    ngx_file_t      *file;     // 引用的文件
    ....
};

ngx_file_t表示一个文件:
typedef struct ngx_file_s        ngx_file_t;
struct ngx_file_s {
    ngx_fd_t                   fd;              // 文件描写叙述符
    ngx_str_t                  name;            // 文件名称
    ngx_file_info_t            info;            // 文件相关信息,相当于stat结构体
 
    off_t                      offset;          // 告诉Nginx处理到文件何处了。一般不使用
    off_t                      sys_offset;      // 文件偏移量,一般不使用
 
    ngx_log_t                 *log;             // 日志对象
 
#if (NGX_HAVE_FILE_AIO)
    ngx_event_aio_t           *aio;
#endif
 
    unsigned                   valid_info:1;    // 未使用
    unsigned                   directio:1;      // 发送大文件时设为1
};

发送响应包体之前就须要对上述部分成员进行设置。以确定须要发送的文件和相关信息。

为了防止内存泄漏,HTTP框架须要在发送响应结束之后关闭文件描写叙述符,这是须要定义一个ngx_pool_cleanup_t结构体:
struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;  // 运行实际清理工作的回调方法
    void                 *data;     // 回调方法的參数
    ngx_pool_cleanup_t   *next;     // 下一个清理对象
};

清理文件句柄的完整代码例如以下:
// 用于告诉HTTP框架,请求结束时调用cln->handler成员函数
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专用于关闭文件句柄
     
ngx_pool_cleanup_file_t  *clnf = cln->data; // cln->data为上述回调函数的參数
clnf->fd = b->file->fd;
clnf->name = b->file->name.data;
clnf->log = r->pool->log;

handler成员设为了ngx_pool_cleanup_file函数,这是Nginx本身内置的一个函数:
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_t指针。该结构体定义例如以下:
typedef struct {
    ngx_fd_t              fd;   // 文件描写叙述符
    u_char               *name; // 文件名称
    ngx_log_t            *log;  // 日志对象
} ngx_pool_cleanup_file_t;

这三个成员和ngx_buf_t.ngx_file_t中的下面三个成员一一相应:
  • ngx_buf_t.ngx_file_t.df
  • ngx_buf_t.ngx_file_t.name
  • ngx_buf_t.ngx_file_t.log
所以我们仅仅须要用ngx_buf_t内的成员对ngx_pool_cleanup_file_t结构体赋值就可以。


程序总体框架和从内存发送数据同样,以下是完整的代码:
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
 
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
 
static ngx_command_t ngx_http_mytest_commands[] =
{
    {
        ngx_string("mytest"),
        NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
        ngx_http_mytest,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
 
    ngx_null_command
};
 
static ngx_http_module_t  ngx_http_mytest_module_ctx =
{
    NULL,                  /* preconfiguration */
    NULL,                  /* postconfiguration */
 
    NULL,                  /* create main configuration */
    NULL,                  /* init main configuration */
 
    NULL,                  /* create server configuration */
    NULL,                  /* merge server configuration */
 
    NULL,                /* create location configuration */
    NULL                   /* merge location configuration */
};
 
ngx_module_t  ngx_http_mytest_module =
{
    NGX_MODULE_V1,
    &ngx_http_mytest_module_ctx,           /* module context */
    ngx_http_mytest_commands,              /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};
 
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t  *clcf;
 
    // 首先找到mytest配置项所属的配置块,clcf貌似是location块内的数据
    // 结构,事实上不然。它能够是main、srv或者loc级别配置项,也就是说在每一个
    // http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
 
    // http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时。假设
    // 请求的主机域名、URI与mytest配置项所在的配置块相匹配,就将调用我们
    // 实现的ngx_http_mytest_handler方法处理这个请求
    clcf->handler = ngx_http_mytest_handler;
 
    return NGX_CONF_OK;
}
 
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
    // 必须是GET或者HEAD方法,否则返回405 Not Allowed
    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
        return NGX_HTTP_NOT_ALLOWED;
 
    // 丢弃请求中的包体
    ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK)
        return rc;
 
    ngx_buf_t *b;
    b = ngx_palloc(r->pool, sizeof(ngx_buf_t));
 
    u_char* filename = (u_char*)"/tmp/test.txt";   // 要打开的文件名称
    b->in_file = 1;     // 设置为1表示缓冲区中发送的是文件
 
    // 分配代表文件的结构体空间。file成员表示缓冲区引用的文件
    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
    b->file->fd = ngx_open_file(filename, NGX_FILE_RDONLY | NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0);
    b->file->log = r->connection->log;  // 日志对象
    b->file->name.data = filename;      // name成员表示文件名称称
    b->file->name.len = sizeof(filename) - 1;
    if (b->file->fd <= 0)
        return NGX_HTTP_NOT_FOUND;
 
    r->allow_ranges = 1;    //支持断点续传
 
    // 获取文件长度,ngx_file_info方法封装了stat系统调用
    // info成员就表示stat结构体
    if (ngx_file_info(filename, &b->file->info) == NGX_FILE_ERROR)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
 
    // 设置缓冲区指向的文件块
    b->file_pos = 0;                        // 文件起始位置
    b->file_last = b->file->info.st_size;   // 文件结束为止
 
    // 用于告诉HTTP框架。请求结束时调用cln->handler成员函数
    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专用于关闭文件句柄
     
    ngx_pool_cleanup_file_t  *clnf = cln->data; // cln->data为上述回调函数的參数
    clnf->fd = b->file->fd;
    clnf->name = b->file->name.data;
    clnf->log = r->pool->log;
 
    // 设置返回的Content-Type
    // 注意,ngx_str_t有一个非常方便的初始化宏
    // ngx_string,它能够把ngx_str_t的data和len成员都设置好
    ngx_str_t type = ngx_string("text/plain");
 
    //设置返回状态码
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = b->file->info.st_size;    // 正文长度
    r->headers_out.content_type = type;
 
    // 发送http头部
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
        return rc;
 
    // 构造发送时的ngx_chain_t结构体
    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;
 
    //最后一步发送包体,http框架会调用ngx_http_finalize_request方法
    return ngx_http_output_filter(r, &out);
}

依据程序的指示。在/tmp文件夹内创建test.txt文件,内容例如以下:
【Nginx】磁盘文件写入飞地发


执行结果:
【Nginx】磁盘文件写入飞地发

Nginxserver成功返回了test.txt文件内的内容。

參考:
《深入理解Nginx》 P107-P112.

版权声明:本文博客原创文章,博客,未经同意,不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/117562.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【⚠️windows删除文件夹抽风了⚠️】“错误0x80070091:目录不是空的”问题处理

    有时候我们想要删除一些无用的文件,但是删除的时候缺发现无法删除文件提示“错误0x80070091:目录不是空的”的问题,下面小编就详细的为大家介绍无法删除空文件夹提示“错误0x80070091:目录不是空的”通用解决方法。具体解决方法如下:1、首先呢,我们随便复制一个文件。(比如我复制自己桌面这个图片)2、“BT”这个文件夹根目录有两个文件夹。3、打开第一个文件夹,一直打开到最后一个文件夹,粘贴之前复制的文件。4、打开第二个文件…

    2022年4月9日
    223
  • SpringBoot实现文件上传接口[通俗易懂]

    SpringBoot实现文件上传接口[通俗易懂]摘要公司都是采用SpringBoot作为项目框架,其实SpringBoot和SSM框架很接近,基本上只是将SSM的一些配置项修改为自动配置或者简单的注解配置就可以了,建议不了解的SpringBoot的朋友们可以了解一下,上手很快,其实文件上传框架根本没有多大关系。我只是顺便帮SpringBoot打个广告罢了。正题需求:需要实现一个文件上传的web接口。1、先实现一个Controll…

    2022年6月12日
    57
  • thinkphp3.2配置redis缓存和文件缓存

    thinkphp3.2配置redis缓存和文件缓存

    2021年10月26日
    84
  • pycharm许可证过期_当前系统license过期

    pycharm许可证过期_当前系统license过期问题描述今天打开PyCharm的时候弹出提示框Yourlicensehasexpired,表示证书到期了,无法继续使用软件。之前使用的证书是在学校的时候注册的学生账号,也就是以.edu.cn结尾的账号。现在需要寻找一种新的认证方式。解决方案PyCharm属于JetBrains的一员,这个系列的软件的认证方式有三种,分别是账号登录、激活码和授权服务器。对于在校的学生用户,…

    2022年8月25日
    8
  • 行为识别数据集汇总[通俗易懂]

    行为识别数据集汇总[通俗易懂]工欲善其事,必先利其器http://www.cs.utexas.edu/~chaoyeh/web_action_data/dataset_list.html,此链接内容更全,可惜整理完后发现的。1.TheKTHDataset(2004)KTH数据集于2004年的发布,是计算机视觉领域的一个里程碑。此后,许多新的数据库陆续发布。数据库包括在4个不同场景下25个人完成的6…

    2022年6月21日
    74
  • 服务器系统防盗,Windows系统中IIS防盗链设置详细介绍Windows服务器操作系统 -电脑资料…

    服务器系统防盗,Windows系统中IIS防盗链设置详细介绍Windows服务器操作系统 -电脑资料…在Windows系统中IIS防盗链设置需一个ISAPI_Rewrite组件,然后我们把ISAPI_Rewrite加载到iis中,再就可以在iis中的httpd.ini中写防盗链功能了,下面我来给各位同学介绍,首页我们安装一个组件:isapi.msi安装完后,对软件安装目录的IIS_WGP组的读写权限(重要,如果不设置安装完后你的网站就会直接ServiceUnavailable,无法访问)。假如你…

    2022年7月23日
    11

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号