设为首页 收藏本站
查看: 924|回复: 0

[经验分享] 如何在nginx中读取POST上来的数据

[复制链接]

尚未签到

发表于 2015-7-26 12:27:47 | 显示全部楼层 |阅读模式
  nginx中对POST数据的读取是异步进行的,也就是说你不必在content handler中等待数据读完然后返回。对客户端的响应是通过:
  ngx_http_send_header(r);
ngx_http_output_filter(r,&out);
  两个调用完成,content handler的return并不意味着请求处理的完成。
  既然是异步调用,而且caller可以立即返回,那就意味着需要定义一个回调函数:
  typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r)
  
  真正进行数据读取的是这个函数ngx_http_read_client_request_body,这个函数有两个参数,一个是request_rec另外一个就是回调函数指针。
  
  在ngx_http_read_client_request_body这个函数中,进行一系列的检查和空间分配之后,
  
  //当content-length为0时,nginx直接调用回调函数。
  if (r->headers_in.content_length_n == 0) {
  …
  }
  接下来,nginx会先处理已经读进来的一些数据,通过计算r->header_in->last - r->header_in->pos的值来判断是否有已读入且未处理的数据。
  如果有这样的数据那么会申请一块新的buf:
  b = ngx_calloc_buf(r->pool);
  然后将读入的数据映射到这个buf中:
  b->temporary = 1;      
b->start = r->header_in->pos;      
b->pos = r->header_in->pos;      
b->last = r->header_in->last;      
b->end = r->header_in->end;
  然后申请一块buf chain:
  rb->bufs = ngx_alloc_chain_link(r->pool);
  将刚创建的buf加入buf chain:
  rb->bufs->buf = b;      
rb->bufs->next = NULL;
  在将数据buffer完成后,就要看下是否所有的数据都已经读进来了:
  if ((off_t) preread >= r->headers_in.content_length_n)
  如果此时所有的数据都已经读进来了那么就直接处理掉就好了:
  r->header_in->pos += (size_t) r->headers_in.content_length_n;      
r->request_length += r->headers_in.content_length_n;      
b->last = r->header_in->pos;
  if (r->request_body_in_file_only) {   
             if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) {     
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;     
             }     
         }
  post_handler(r);
  如果还有数据没有读到:
  这句是将pos指针移到目前读到的数据末尾,保证每次buffer掉数据后,pos始终指向数据末尾的位置。
  r->header_in->pos = r->header_in->last;
  然后看下还有多少没有读到:
  rb->rest = r->headers_in.content_length_n - preread;
  这里需要做个判断,如果没读到的数据比当前申请到的buf空间都大的话,那么就需要重新申请一块buffer了:
  next = &rb->bufs->next;
  
  否则的话就进入:
  ngx_http_do_read_client_request_body
  读取content body
  
  到这里,nginx已经为content_body分配好了空间并读入了随着请求过来的部分数据。由于只能在读到header之后才知道数据的实际大小并且request_rec中业已拷贝了,所以要么在读到header的时候分配好需要的buf大小,然后执行拷贝,将preread的数据拷贝到这个buf里,要么区别处理,即不分配preread到的数据,因为这部分的数据已经拷贝进了header_in,这种情况下,只需分配一个buf,将其映射到header_in中的数据区,然后对于剩下的尚未读取到的数据分配新的buf,并将其链入buf chain。
  显而易见,后者会节省一次拷贝操作,并且对于特定应用,如果预分配的空间足够大,那么完全不需要第二次的考虑操作。nginx用的策略就是后者。
  
  
  上面的流程其实是一个优化处理的结果,即在content body随着request一起提交上来时,就无需设置event并回调,直接处理掉就好了,和GET方法一样。
  
  如果在收到请求header时,并非所有的数据都已经提交上来,那么就需要类似上面的处理,
  为r->request_body->buf申请空间,注意这里在分配空间时,并非完全根据content_length进行,是有上限的,而且一经分配就进入读取阶段,并不会再次分配。结合上面的内容,可见,对于POST数据,最多有两个buf。
  rb->buf = ngx_create_temp_buf(r->pool, size);
  然后申请一个buffer chain,并将buffer chain的第一个buf指向我们刚申请的rb->buf:
  cl = ngx_alloc_chain_link(r->pool);     
  if (cl == NULL) {      
      return NGX_HTTP_INTERNAL_SERVER_ERROR;      
  }
  cl->buf = rb->buf;     
cl->next = NULL;
  最后通过:
  *next = cl;
  cl初始化完成后就可以将其值赋值给r->request_body->bufs。
  
  并最终同样进入:
  ngx_http_do_read_client_request_body
  这个函数进行实际的数据读取工作,逻辑很简单:调用recv,一直读到返回again或error,或已经读完了所有数据,error的时候直接返回,again表明还有数据没读则设置一个超时器,加入读事件进入事件队列,然后等待下次进程调度执行。
  如果已经读完了,那么先删掉上次的定时器。如果
  rb->temp_file || r->request_body_in_file_only
  两个值被设置了,那么就将request_body保存进一个临时文件中。
  最后,带着已经读取完毕的post数据,调用我们设置的post_handler回调函数:
  rb->post_handler(r);
  相信读到这里,POST数据如何获取已经一目了然了。另外,如果设置了标记将post数据写入file的话,存放file信息的buf最终会链入bufs chain中。
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-90788-1-1.html 上篇帖子: nginx中将POST数据写到日志里面的正确方式 下篇帖子: nginx 截断日志一个批处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表