nginx丢弃http包体处理
http框架丢弃http请求包体和上一篇文章http框架接收包体, 都是由http框架提供的两个方法,供http各个模块调用,从而决定对包体做什么处理。是选择丢弃还是接收,都是由模块决定的。例如静态资源模块,如果接收到来自浏览器的get请求,请求某个文件时,则直接返回这个文件内容给浏览器就可以了。没有必要再接收包体数据,get请求实际上也不会有包体。因此静态资源模块将调用http框架提供的丢弃包体函数进行丢包处理。
相比接收包体过程, 丢弃包体操作就简单很多了,至少不需要把包体存放到http结构中的request_body缓冲区,也不需要考虑包体是否只存放到内存,或者只存放到文件中等问题, 框架接收完包体后就直接丢弃了。丢弃包体由三部分组成:
(1) http模块首次调用框架提供的ngx_http_discard_request_body函数,做些初始化操作。例如如果一次操作无法丢弃所有包体 ,则需要重新把读事件注册到epoll中,这样再次调度执行时,能够继续执行丢包操作。再者,调用实际的丢包函数ngx_http_read_discarded_request_body进行丢弃包体操作。
(2)如果一次操作无法丢弃所有包体,则在事件再次被调度时,继续接收剩余的包体数据,然后丢弃。
(3)实际的丢包处理,也就是接收包体后,直接丢弃。
从图中可以看出这三个过程中,丢包流程是一个公共的功能。也就是说不管http模块调用ngx_http_discard_request_body函数开始进行丢包处理,还是一次调度没有接收完全部包体时,由ngx_http_discarded_request_body_handler负责丢弃剩余的包体操作, 都会调用公共的丢包函数ngx_http_read_discarded_request_body进行接收包体后直接丢弃操作。
一、丢包初始化流程
ngx_http_discard_request_body是被http模块调用,用于丢弃包体的函数。对于模块来讲是一个透明的操作。也就是说模块只需要调用这个接口就可以丢弃http请求包体,而不需要知道http框架是如何实现这个接口的。纵使框架一次调度没有丢弃完所有包体,下一次调度执行时会再次进行丢包操作,但对模块来说,他们是不知道的。
[cpp] view plain copy
//功能: 丢弃http包体的首次回调函数,如果一次性不能全部接收完成并丢弃,则设置
// 读事件的回调为ngx_http_discarded_request_body_handler
ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r)
{
//需要丢弃的包体不用考虑超时问题
if (rev->timer_set)
{
ngx_del_timer(rev);
}
//包体长度小于等于0,则直接返回。表示丢弃包体
//如果已经接收过包体了,这时也不需要在接收。通常情况下get请求没有包体,因此包体长度为0
if (r->headers_in.content_length_n <= 0 || r->request_body)
{
return NGX_OK;
}
size = r->header_in->last - r->header_in->pos;
//已经预先接收了部分包体
if (size)
{
//包体未全部接收完成
if (r->headers_in.content_length_n > size)
{
r->header_in->pos += size;
r->headers_in.content_length_n -= size;
}
else
{
//包体已经全部接收
r->header_in->pos += (size_t) r->headers_in.content_length_n;
r->headers_in.content_length_n = 0;
return NGX_OK;
}
}
//设置后续读事件的回调
r->read_event_handler = ngx_http_discarded_request_body_handler;
//注册读事件回调,插入到epoll
ngx_handle_read_event(rev, http://blog.sina.com.cn/u/5080116125 0));
//接收包体内容
if (ngx_http_read_discarded_request_body(r) == NGX_OK)
{
//表示已经接收到完整的包体了,将延迟关闭清0
r->lingering_close = 0;
}
else
{
//表示需要多次调度才能完成丢弃包体这个操作,于是把引用计数加1,防止这边在丢弃包体,而其他
//事件却已经让请求意外销毁
r->count++;
//标识为正在丢弃包体
r->discard_body = 1;
}
return NGX_OK;
}
在接收http请求头部时,如果也顺便接收了http包体数据,这个时候就没有必要继续执行剩余的操作,丢弃包体成功,函数直接返回。如果一次调度没有丢弃完所有包体,则会设置http请求结构ngx_http_request_s的读事件read_event_handler为:ngx_http_discarded_request_body_handler, 下一次被调度时由这个函数负责丢弃剩余的包体。因此ngx_http_discard_request_body只会被http模块首次调用。
函数也会调用实际的丢包函数ngx_http_read_www.wmyl11.com discarded_request_body开始进行接收包体后直接丢弃处理。
二、丢包处理
ngx_http_read_discarded_request_body函数负责接收来自客户端的包体数据,然后再丢弃。因此对于模块而言,就是丢弃包体操作,但对于框架而言,丢弃包体操作其实就是接收包体操作, 只不过接收后的包体数据没有交给模块使用而已。为什么框架要接收包体后再直接丢弃呢? 岂不是多此一举。其实不然,之所以这样做是有原因的。假设某个不健壮的客户端浏览器使用阻塞的方法向nginx服务器发送了http包体数据, 如果nginx框架不接收的话,会导致客户端浏览器超时没有反应,从而导致客户端浏览器关闭这个连接。因此nginx的http框架要先从内核中接收来自客户端的包体数据, 但这些数据对于模块而言是没有用的,因此接收后的这些数据会直接被丢弃。
[cpp] view plain copy
//功能: 从内核中读取数据到nginx中,nginx不对收到的数据进行处理。相当于丢弃包体
static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
//用于接收包体的临时缓冲区
u_char buffer[NGX_HTTP_DISCARD_ www.wmyl88.com BUFFER_SIZE];
for ( ;; )
{
//已经全部丢弃成功
if (r->headers_in.content_length_n == 0)
{
//设置丢弃后的读事件回调,再有读事件时,不做任何处理
r->read_event_handler = ngx_http_block_reading;
return NGX_OK;
}
//从内核中接收包体到临时缓冲区
n = r->connection->recv(r->connection, buffer, size);
//更新剩余需要接收的包体大小
r->headers_in.content_length_n -= n;
}
}
函数内部只是使用一个临时的缓冲区变量存放每次接收来自内核的包体数据。并没有把这部分数据保存到http请求结构中的request_body缓冲区。因此包体数据没有交给http模块,相当于被丢弃了。在所有包体从内核中接收完成时,设置http请求结构ngx_http_request_s的读事件read_event_handler回调设置为: ngx_http_block_reading, 表示再收到来自客户端的数据,则不进行任何处理了。因为已经接收完所有的包体数据,也就不需要理会来自客户端浏览器的其它数据。
三、丢弃剩余的包体
ngx_http_discarded_request_body_handler用于在一次调度中没有丢弃完所有包体,则该函数会表调用,用于丢弃剩余的包体。函数内部也会调用实际的丢弃包体函数,进行接收包体然后丢弃操作。nginx服务器做了一个优化处理,会设置一个总超时时间,如果超过这个时间都还没有丢弃完全部的包体,则会关闭这个连接。这是一种对服务器保护的措施,避免长时间的丢包操作占用服务器资源。
[cpp] view plain copy
//功能: 第1次未能全部丢弃包体时,该函数被调用。之后有读事件时,该函数被调用
void ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
{
//检测延迟关闭时间,如果总时长超过了lingering_time,则不再接收任何包体,这是一个总时间。
//总超时后,将直接光比连接
if (r->lingering_time)
{
timer = (ngx_msec_t) (r->lingering_time www.feifanshifan8.cn- ngx_time());
//已经到达了延迟关闭时间
if (timer <= 0)
{
//清空丢弃包体标识,表示包体已经丢弃
r->discard_body = 0;
//延迟关闭开关清0
r->lingering_close = 0;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
}
//接收包体后丢弃
rc = ngx_http_read_discarded_request_body(r);
//表示包体已经全部丢弃
if (rc == NGX_OK)
{
r->discard_body = 0; //包体已经全部接收完
r->lingering_close = 0; //清空延迟关闭标志
ngx_http_finalize_request(r, NGX_DONE);
return;
}
}
ngx_http_discarded_request_body_handler这个函数是怎么被事件对象调用的呢? 在前面的文章已经分析了,ngx_connection_s读事件的回调设置为ngx_http_request_handler。 因此在读事件发生时,会回调请求结构的读回调。如果还不是不清楚这个调用过程,可以参考: nginx处理http请求这篇文章
[cpp] view plain copy
static void ngx_http_request_handler(ngx_event_t *ev)
{
//如果同时发生读写事件,则只有写事件才会触发。写事件优先级更高
if (ev->write)
{
r->write_event_handler(r www.yongshiyule178.com); //在函数ngx_http_handler设置为ngx_http_core_run_phases
}
else
{
r->read_event_handler(r); //在函数ngx_http_process_request设置为ngx_http_block_reading
}
}
到此为止,http框架丢弃包体的流程已经分析完成了。下一篇文章将分析如何结束一个http请求。
nginx丢弃http包体处理的更多相关文章
- Nginx 反向代理接收用户包体方式
陶辉91课 如果proxy_request_buffering 设置为on的时候是等待nginx读取完包体后再发送上游服务器 一般依赖于nginx处理能力 client_body_in_file_o ...
- nginx的ngx_http_request_t结构体
struct ngx_http_request_s { uint32_t signature; /* "HTTP" */ //请求对应的客户端连接 ngx_connection_t ...
- 深入理解 web 协议(一)- http 包体传输
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/WlT8070LlrnSODFRDwZsUQ作者:吴越 开坑这个系列的原因,主要是在大前端学习的 ...
- http请求报文格式(请求行、请求头、空行 和 请求包体)和响应报文格式(状态行、响应头部、空行 和 响应包体)
转载 出处 超文本传输协议(Hypertext Transfer Protocol,简称HTTP)是应用层协议.HTTP 是一种请求/响应式的协议,即一个客户端与服务器建立连接后,向服务器发送一个请求 ...
- 【原创】ORA-04068: 已丢弃程序包 的当前状态研究
不久前在市检的生产环境上有个存储过程执行报错,错误信息如下: ORA: 已丢弃程序包 的当前状态 ORA: package "ZHANGXSH.PR_TEST" 的当前状态失效 O ...
- PL/SQL 包头和包体
包用于逻辑组合相关的过程和函数,它由包规范和包体两部分组成,包规范用于定义公用的常量 变量,过程和函数,在SQL*PLUS中建立包规范可以使用CREATE PACKAGE命令. 实例如下: CREAT ...
- PL/SQL 编程(三 )程序包和包体,触发器,视图,索引
一.程序包和包体 程序包(package):存储在数据库中的一组子程序.变量定义.在包中的子程序可以被其它程序包或子程序调用.但如果声明的是局部子程序,则只能在定义该局部子程序的块中调用该局部子程序. ...
- 使用rpm-build制作nginx的rpm包
2014-11-27 11:05:49 一.RPM包的分类 RPM有五种基本的操作功能:安装.卸载.升级.查询和验证. linux软件包分为两大类: (1)二进制类包,包括rpm安装包(一般分为i ...
- oracle 之 包,包体创建和使用案例
先创建包,再创建包体---------------创建包体--------------------- create or replace package body pkg_yygl_service I ...
随机推荐
- Spring JdbcTemplate详解(转)
原文地址:http://www.cnblogs.com/caoyc/p/5630622.html 尊重原创,请访问原文地址 JdbcTemplate简介 Spring对数据库的操作在jdbc上面做 ...
- Python基础-list,tuple,dict,set常用方法及区别
1,列表list 列表定义方式 lis1=[1,2,3,4]#一维数组 lis2=[1,2,3,[4,5,6]]#二维数组 依次多有多维数据,套几层就是几维数组 列表的取值可以通过下标来,下标都是从0 ...
- SQL的CASE表达式用法
case 表达式从SQL-92标准开始引入,因此是不依赖于具体的数据库技术,可提高SQL代码的可移植性. case表达式注意事项: 1. 统一各个分支返回数据类型,并保证各个when字句的排他性,因为 ...
- C++内存使用机制基本概念详解
.程序使用内存区 一个程序占用的内存区一般分为5种: ()全局.静态数据区:存储全局变量及静态变量(包括全局静态变量和局部静态变量) ()常量数据区:存储程序中的常量字符串等. ()代码区:存储程序的 ...
- 幻想乡三连B:连在一起的幻想乡
$G[k][x]$表示所有$x$个点的无向图中每一个图的边数的$k$次方之和. $F[k][x]$就是在$G[k][x]$的基础上加了一个整体连通的性质. 有一个经典的套路就是对于$F$在对应的$G$ ...
- JSTL1.0 知识回顾与总结
JSTL1.0,由四个定制标志库(core,format,xml和sql) 和一对通用标记库验证器(ScriptFreeTLV 和 PermittedTaglibsTLV )组成. 1.基本输出设置操 ...
- Mybatis generator配置文件及说明
项目采用sring mvc + mybatis 组合,这里简单介绍下mybatis的应用: 我的IDE是STS(Spring + Tool + Suite), 安装Mybatis Generator插 ...
- @@cursor_rows变量解析
刚刚看了@@curosr_rows这个全局变量,发现这个变量挺有意思.要懂得这个变量的意义,基本上牵扯到cursor一些比较容易忽视的内容. @@cursor_rows是用来记录当前游标的数量,也就从 ...
- [转]Google开源Leak Finder—用于检测内存泄漏的JavaScript工具-----可惜,暂时打不开google的网站,下载不了
近日,Google开源了Leak Finder,这款工具可以查看JavaScript应用的堆,进而发现内存泄漏. 作为一门垃圾收集语言,JavaScript并不会出现常见的内存泄露情况,特别是像C++ ...
- Swift访问控制
参考博客原文链接 http://www.jianshu.com/p/604305a61e57 http://www.hangge.com/blog/cache/detail_524.html 我的总结 ...