Nginx丢弃http包体处理实例详解

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框架是如何实现这个接口的。纵使框架一次调度没有丢弃完所有包体,下一次调度执行时会再次进行丢包操作,但对模块来说,他们是不知道的。

//功能: 丢弃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, 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_discarded_request_body开始进行接收包体后直接丢弃处理。

二、丢包处理

ngx_http_read_discarded_request_body函数负责接收来自客户端的包体数据,然后再丢弃。因此对于模块而言,就是丢弃包体操作,但对于框架而言,丢弃包体操作其实就是接收包体操作, 只不过接收后的包体数据没有交给模块使用而已。为什么框架要接收包体后再直接丢弃呢? 岂不是多此一举。其实不然,之所以这样做是有原因的。假设某个不健壮的客户端浏览器使用阻塞的方法向nginx服务器发送了http包体数据, 如果nginx框架不接收的话,会导致客户端浏览器超时没有反应,从而导致客户端浏览器关闭这个连接。因此nginx的http框架要先从内核中接收来自客户端的包体数据, 但这些数据对于模块而言是没有用的,因此接收后的这些数据会直接被丢弃。

//功能: 从内核中读取数据到nginx中,nginx不对收到的数据进行处理。相当于丢弃包体
static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
 //用于接收包体的临时缓冲区
 u_char buffer[NGX_HTTP_DISCARD_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服务器做了一个优化处理,会设置一个总超时时间,如果超过这个时间都还没有丢弃完全部的包体,则会关闭这个连接。这是一种对服务器保护的措施,避免长时间的丢包操作占用服务器资源。

//功能: 第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 - 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请求这篇文章

static void ngx_http_request_handler(ngx_event_t *ev)
{
 //如果同时发生读写事件,则只有写事件才会触发。写事件优先级更高
 if (ev->write)
 {
  r->write_event_handler(r); //在函数ngx_http_handler设置为ngx_http_core_run_phases
 }
 else
 {
  r->read_event_handler(r); //在函数ngx_http_process_request设置为ngx_http_block_reading
 }
}

到此为止,http框架丢弃包体的流程已经分析完成了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • 详解nginx同一端口监听多个域名和同时监听http与https

    详解nginx同一端口监听多个域名和同时监听http与https 1.同一端口监听多个域名 如今公网ip资源越来越珍贵,多域名监听应用非常广泛,就是用一个端口,比如80或者443,监听多个入口域名.如下: server { listen 443 ssl; server_name xxx.xxx.cn; ssl_certificate ssl/server.pem; ssl_certificate_key ssl/server.key; ...... } 在启用新的域名的时候,只需要另启一个ser

  • 详解nginx服务器http重定向到https的正确写法

    http重定向到https使用了nginx的重定向命令.那么应该如何写重定向?之前老版本的nginx可能使用了以下类似的格式. rewrite ^/(.*)$ http://domain.com/$1 permanent; 或者 rewrite ^ http://domain.com$request_uri? permanent; 现在nginx新版本已经换了种写法,上面这些已经不再推荐. 下面是nginx http页面重定向到https页面最新支持的写法: server { listen 80

  • 详解Nginx服务器和iOS的HTTPS安全通信

    详解Nginx服务器和iOS的HTTPS安全通信 简介 在网络通信中,使用抓包软件可以对网络请求进行分析,并进行重放攻击,重放攻击的解决方案一般是使用一个变化的参数,例如RSA加密的时间戳,但考虑到网络传输时延,时间戳需要有一定的误差容限,这样仍然不能从根本上防止重放攻击.想要较好的解决重放攻击问题,应考虑使用HTTPS通信,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输.身份认证的网络协议,比HTTP协议安全. 实现 对于用浏览器访问的网站,需要向CA申请证书才能保证HTTPS的网

  • 详解nginx如何配置HTTPS

    使用ssl模块配置同时支持http和https并存 一,生成证书 # 1.首先,进入你想创建证书和私钥的目录,例如: cd /etc/nginx/ # 2.创建服务器私钥,命令会让你输入一个口令: openssl genrsa -des3 -out server.key 1024 # 3.创建签名请求的证书(CSR): openssl req -new -key server.key -out server.csr # 4.在加载SSL支持的Nginx并使用上述私钥时除去必须的口令: cp se

  • 微信小程序Server端环境配置详解(SSL, Nginx HTTPS,TLS 1.2 升级)

    微信小程序Server环境配置详解 主要内容: 1. SSL免费证书申请步骤 2. Nginx HTTPS 配置 3. TLS 1.2 升级过程 微信小程序要求使用 https 发送请求,那么Web服务器就要配置成支持 https,需要先申请SSL证书 小程序也要求 TLS(传输层安全协议)的版本至少为 1.2,在配置好 https之后,如果 TLS 的版本较低,就涉及到升级问题 所以 Server端环境配置的主要步骤: 申请 SSL 证书 配置web服务器支持https(我使用的是nginx)

  • nginx处理http请求实例详解

    本文在这基础上分析nginx服务器收到http请求行.请求头部后,http框架是如何调度各个http模块共同完成这个http请求.例如: http框架调度静态模块,获取服务器目录下的某个html页面返回给客户端: 或者http框架调度access权限访问模块,判断这个客户端是否有权限访问服务器. 一.event事件与http框架的交互 在接收完http请求行.http请求头部后,会调用ngx_http_process_request这个函数开始处理http请求.因为一个http请求由11个处理阶

  • 详解NGINX访问https跳转到http的解决方法

    问题:浏览器打开https://www.jb51.net/aaa.html,然后跳转到http://www.jb51.net/aaa.html 网站架构:用户--https--->nginx代理---http---->tomcat/nginx+php nginx待遇发给后端的请求是http协议,后端程序跳转获取到的协议是http,返回一个redirect(http header中带Location:http://www.jb51.net/aaa.html),浏览器收到location,跳转到了

  • 详解阿里云LINUX服务器配置HTTPS(NGINX)

    背景说明 服务器为阿里云 ECS,操作系统为 CentOS 6.5. 部署配置说明 第一步,安装nginx 之所以要先安装 nginx,是因为下面配置域名解析的时候可以直接在浏览器看到效果,当然了,先配置域名,然后 ping 一下也是可以的 下载Nginx源码包,解压源码包,进入解压后的目录,编译配置,命令如下: ./configure --prefix=/usr/local/nginx --with-http_ssl_module 以上命令将nginx安装到usr/local/nginx目录下

  • Nginx丢弃http包体处理实例详解

    Nginx丢弃http包体处理实例详解 http框架丢弃http请求包体和上一篇文章http框架接收包体, 都是由http框架提供的两个方法,供http各个模块调用,从而决定对包体做什么处理.是选择丢弃还是接收,都是由模块决定的.例如静态资源模块,如果接收到来自浏览器的get请求,请求某个文件时,则直接返回这个文件内容给浏览器就可以了.没有必要再接收包体数据,get请求实际上也不会有包体.因此静态资源模块将调用http框架提供的丢弃包体函数进行丢包处理. 相比接收包体过程, 丢弃包体操作就简单很

  • Python科学计算包numpy用法实例详解

    本文实例讲述了Python科学计算包numpy用法.分享给大家供大家参考,具体如下: 1 数据结构 numpy使用一种称为ndarray的类似Matlab的矩阵式数据结构管理数据,比python的列表和标准库的array类更为强大,处理数据更为方便. 1.1 数组的生成 在numpy中,生成数组需要指定数据类型,默认是int32,即整数,可以通过dtype参数来指定,一般用到的有int32.bool.float32.uint32.complex,分别代表整数.布尔值.浮点型.无符号整数和复数 一

  • nginx下支持PATH_INFO的方法实例详解

    本文实例分析了nginx下支持PATH_INFO的方法.分享给大家供大家参考,具体如下: 要想让nginx支持PATH_INFO,首先需要知道什么是pathinfo,为什么要用pathinfo? pathinfo不是nginx的功能,pathinfo是php的功能. php中有两个pathinfo,一个是环境变量$_SERVER['PATH_INFO']:另一个是pathinfo函数,pathinfo() 函数以数组的形式返回文件路径的信息;. nginx能做的只是对$_SERVER['PATH

  • nginx centos 服务开机启动设置实例详解

    nginx centos 服务开机启动设置 建立服务文件 以nginx 为例 vim /lib/systemd/system/nginx.service 在nginx.service 中插入一下内容 [Unit] Description=nginx After=network.target [Service] Type=forking ExecStart= 服务启动命令 ExecReload= 服务重启命令 ExecStop=服务停止命令 PrivateTmp=true [Install] Wa

  • CentOS6.3添加nginx系统服务的实例详解

    CentOS6.3添加nginx系统服务的实例详解 前言: 今天虚拟机上配了下服务器整理了个这个 nginx 服务 要注意 - 短横杠这个符号看看复制进去后有没有乱码,我之前就遇到这个问题,郁闷了好久才发现 提示:顶部的注释不要去除否则无法注册为系统服务, 关于:chkconfig: 2345 65 37 网上搜索总结了下意思是: 2345 为启动该服务的系统环境 65   为加载的优先级别 37   为关闭的优先级别 65,37 这两个位置的数值不能相同,也不能和其它服务的数值冲突,这个我也没

  • ThinkPHP 在阿里云上的nginx.config配置实例详解

    具体代码如下所示: # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes auto; error_log /var/log/nginx/error.log;

  • java动态添加外部jar包到classpath的实例详解

    java动态添加外部jar包到classpath的实例详解 前言: 在项目开发过程中我们有时候需要动态的添加外部jar包,但是具体的业务需求还没有遇到过,因为如果动态添加外部jar包后,我们就需要修改业务代码,而修改代码就需要重新启动服务,那样好像就没有必要动态添加外部jar包了,怎么样才能不重新启动服务器就可以使用最新的代码我没有找到方法,如果各位知道的话给我点建议,回归主题,实现动态添加外部jar包到classpath的方法如下: String beanClassName = "com.dy

  • java ant包中的org.apache.tools.zip实现压缩和解压缩实例详解

    java ant包中的org.apache.tools.zip实现压缩和解压缩实例详解 其实apache中的ant包(请自行GOOGLE之ant.jar)中有一个更好的类,已经支持中文了,我们就不重复制造轮子了,拿来用吧, 这里最主要的功能是实现了 可以指定多个文件 到同一个压缩包的功能 用org.apache.tools.zip压缩/解压缩zip文件的例子,用来解决中文乱码问题. 实例代码: import Java.io.BufferedInputStream; import java.io.

  • Linux Nginx 配置SSL访问实例详解

    Linux Nginx 配置SSL访问实例详解 生成证书 可以通过以下步骤生成一个简单的证书: 首先,进入你想创建证书和私钥的目录,例如: $ cd /usr/local/nginx/conf 创建服务器私钥,命令会让你输入一个口令: $ openssl genrsa -des3 -out server.key 1024 创建签名请求的证书(CSR): $ openssl req -new -key server.key -out server.csr 在加载SSL支持的Nginx并使用上述私钥

  • Nginx 多站点配置实例详解

    Nginx 多站点配置实例详解 在一台 VPS 上,我们有时候需要同时跑几个 virtualenv.比如 virtualenv app1 跑的是 Django 的一个应用,而 virtualenv app2 跑的是 Tornado.那么如何配置 Nginx,让它同时支持这两个 virtualenv 的运行呢? 首先是 Nginx 的主配置,位于 etc/nginx/ngnix.conf,让它保持默认就行: user nginx; worker_processes 1; error_log /va

随机推荐