文章继续。什么时候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;
#endifunsigned                   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 Allowedif (!(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文件,内容例如以下:
执行结果:
Nginxserver成功返回了test.txt文件内的内容。
參考:
《深入理解Nginx》 P107-P112.

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

转载于:https://www.cnblogs.com/yxwkf/p/4642517.html

【Nginx】磁盘文件写入飞地发相关推荐

  1. 关于Nginx有没可能漏记请求日志或Nginx重复向后端发请求

    偶然发现有一用户同一时间领取了两份新客券 怀疑客户端未做防重复提交处理, 但问了客户端同事, 确实做了防重复提交处理, 然后查看Nginx日志 发现也只有一条领券日志记录 106.121.xxx.xx ...

  2. ELK系列(四)、Logstash读取nginx日志写入ES中

    前面讲了ELK的部署以及Logstash的插件的安装方式,本篇就介绍一下如何使用Logstash读取nginx的日志,并写入ES中,通过Kibana分析. ELK系列(一).安装ElasticSear ...

  3. ELK学习--将自定义nginx日志写入es中并通过kibana展示为例

    今天只是记录总体思路,具体细节不展开,毕竟东西太多 学习目的 业务发展越来越庞大,服务器越来越多 各种访问日志.应用日志.错误日志量越来越多,导致运维人员无法很好的去管理日志 开发人员排查问题,需要到 ...

  4. docket-compose部署nginx时写入TZ时报错ERROR: yaml.parser.ParserError: while parsing a block collection in xxx

    报错场景 在用docker-compose部署nginx时, 添加了时区TZ参数后报错,错误截图如下: 解决思路 第一时间当然是百度, 最后我发现了很多人的文章都没讲到一件事 : docker-com ...

  5. nginx配置多个server_Nginx基本属性配置详解

    . Nginx服务的基本配置 1.1 用于调试进程和定位问题的配置项 是否以守护进程的方式运行nginx # 默认ondaemon on|off; 是否以master/worker方式工作 # 默认o ...

  6. 26.Nginx详解

    文章目录 一 Web服务基础介绍: 1.1 互联网发展历程回顾: 1.2:web服务介绍: 1.2.1 Apace-早期的web服务端: 1.2.1.3:Apache event模型 1.2.2 Ng ...

  7. nginx的全局配置和HTTP相关配置

    目录 资源类型: 网站访问量 网站访问量统计的重要指标 HTTP1.0和1.1的问题 HTTP2协议 HTTP 请求访问的完整过程 HTTP 请求报文 http协议状态码分类 http协议常用的!!状 ...

  8. 深入操作系统底层分析nginx网络请求及响应过程

    0. 网络传输阶段 比如说主机A是家里windows的一台笔记本电脑,主机B是linux服务器上的一个nginx,其监听80或443等web端口. 在笔记本的浏览器发送了一个http get请求,其数 ...

  9. Nginx截断uwsgi+Django(Flask)大响应体的问题及解决

    目录 症状 找到关键原因 解决 解决2 解决3 推演 症状 昨天一个一直续费的老客户,说网站出问题了.他的网站只是简单的展示型公司官网,用 Django 做的,日常做放放产品,连交易都没有,是2016 ...

最新文章

  1. python如何实现找图_利用OpenCV和Python实现查找图片差异
  2. python 爬虫气象气象定时报 气象预警推送
  3. 由浅到浅入门批量渲染(三)
  4. mysql数据库1067错误
  5. php上传商品信息并显示,第37课 thinkphp5添加商品基本信息及通过前置钩子上传商品主图 模型事件(勾子函数)...
  6. docker开启otter服务mysql单双向同步数据
  7. 极客大学架构师训练营 加密技术 高可用系统的度量 高可用系统的架构 高可用系统的运维 第22课 听课总结
  8. 【2018徐州ICPC Gym-102012 M】Rikka with Illuminations【计算几何】
  9. C#winform连接Access数据库方式
  10. Fastjson实用工具类,List转JSONString,List转JSONArray,JSONArray转List,JSONArray转ArrayList,JSONObject转HashMap
  11. 通过exe解决kindle for pc中文书籍无法修改字体的问题
  12. 网页中嵌入flash文件的几种方法
  13. 跨性別/偽娘/性轉漫畫中譯對照
  14. 一只青蛙一次可以跳上1级台阶,也可以跳上2级,也可以跳n级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
  15. 使用MATLAB2014a将灰度图转为彩色图
  16. Matplotlib绘制三维数据点与线
  17. 中兴V880手机的随机软件列表
  18. 数据结构:10大经典排序
  19. 历史回顾——中国各省省名之由来
  20. pr安装无法启动 计算机丢失,为什么pr安装成功却无法启动?

热门文章

  1. Vue报错Cannot find module ‘webpack-cli/bin/config-yargs‘
  2. set.contains()分析
  3. 关于CSS HACK
  4. 在CentOS7上使用FastDFS搭建文件服务器
  5. 世界互联网大会:华三发安全平台天机
  6. .Net IOC框架入门之一 Unity
  7. Android中SQLiteOpenHelper类的onUpgrade方法浅谈
  8. 教你开发Jquery插件-Jquery插件开发教程
  9. css修改layui的下拉框样式 js_layui,经典模块化前端UI框架,前端菜鸟带你初识栅格。...
  10. python2.7安装pygame_python 安装 pygame了