https://blog.csdn.net/ApeLife

子请求并非http协议标准的实现,可以说是nginx所特有的设计, 为什么需要子请求呢? 一般认为这主要是为了提高nginx内部对单个客户端请求处理的并发能力。如果客户端的某个主请求访问了多个资源(例如通过ssi功能包含了a.html,  b.hmtl、c.html三个资源), 那么对每一处资源访问建立一个子请求并让它们同时进行,效率自然会更高。 所谓的子请求,并不是由客户端直接发起的,它是由nginx服务器在处理客户端请求时,根据自身逻辑需要而内部建立的新请求。因此子请求只在nginx服务器内部进行处理,不会与客户端进行交互。

图:子请求示意图

一、nginx子请求数据结构

(1)树加链表结构

先来整体看下nginx为了支持子请求功能,而设计的数据结构。子请求几乎具备主请求的所有特征,比如有对应完整的ngx_http_request_t结构对象。并且子请求本身也可以发起新的子请求,这是一个嵌套过程。根据子请求的特征,即子请求可以递归的发起子请求(树型结构),  以及同一个请求可以发起多个子请求(链表结构),  因此按照树加链表的形式对它们进行组织是自然而然的事情。先来看下这个树加链表结构:

图: 树加链表结构

对于原始请求,也就是主请求r, 创建了sub1, sub2两个子请求, 而sub2这个子请求本身又创建了两个子请求sub2_1, sub2_2。从图中可以看出子请求之间是通过next指针相互连接构成一个链表, 而父请求与第一个子请求是通过postponed相互连接构成一个树型结构。因此,树加链表的数据结构是这么得来的。因为子请求sub1是最先可以返回响应数据给客户端的请求,如果其他请求完成了与后端服务器通信,产生的响应数据都需要进行缓存。对于图中的r_data是主请求执行完后要发给客户端的响应数据,被缓存起来。sub2_data则是sub2子请求产生的响应数据,被缓存起来。 sub2_1_data则是sub2_1子请求产生的响应数据,被缓存起来。 sub2_2_data则是sub2_2子请求产生的响应数据,被缓存起来。nginx服务器如果对请求产生的数据需要进行缓存,则也会创建一个节点,这个节点用来存放请求产生的数据,而不是存放请求, 然后加入到链表的最末尾。           那什么情况下请求产生的数据需要缓存呢?  因为一个主请求可以创建多个子请求,这些子请求并行与后端服务器通信。但并一定先创建的子请求就一定会最先处理完与后端服务器的通信,因为这是一个异步过程, 是有可能最后创建的子请求却最先完成与后端服务器的通信。nginx服务器会记录哪个子请求是可以最先返回响应数据给客户端浏览器的,因此只要不是这个可以最先返回响应数据给客户端的子请求完成了与后端服务器的通信,这些子请求产生的数据都需要进行缓存。

看下nginx服务器怎么维护这个树加链表的数据结构的:

 
  1. struct ngx_http_request_s

  2. {

  3. //如果是子请求则指向父请求,如果是父请求则为NULL

  4. ngx_http_request_t * parent;

  5. //指向第一个子请求,构成一颗树结构

  6. ngx_http_postponed_request_t * postponed;

  7. }

  8. struct ngx_http_postponed_request_s

  9. {

  10. //指向当前这个请求

  11. ngx_http_request_t *request;

  12. //完成与后端服务器通信后,如果这个请求是不最前面的可以与客户端交互的请求,

  13. //则这个请求产生的响应数据会缓存到out缓冲区中

  14. ngx_chain_t *out;

  15. //指向下一个子请求,构成一个链表

  16. ngx_http_postponed_request_t *next;

  17. };

nginx服务器就是使用上面的这个数据结构构成了一个树加链表的结构。除了这个树加链表的结构外, nginx服务器还维护了一个单项链表结构,目的是为了调度各个子请求进行处理。在ngx_http_run_posted_requests函数被事件机制调用时,将会遍历每一个子请求,包括孙子请求。因此每一个子请求都会被调度执行,子请求在11个http请求阶段中,都会调用相应的http模块共同完成一个子请求。这和原始请求的处理过程是一样的。也可以看出子请求是原始请求的一个派生,可以执行和原始请求一样的操作。

(2)单项链表结构

图: 原始请求维护的子请求单链表

对于树加链表结构中的每一个子请求节点(注意: 不包括数据节点,例如r_data,这些数据节点是不会加入到原始请求的链表末尾), 都会加入到主请求,也就是原始请求的posted_requests链表末尾。为什么要这么做呢? 就是为了在这事件循环中,遍历这个链表可以调度所有的子请求进行处理,使得每一个子请求都有机会被执行。如果读者对这块逻辑不是很清楚也不要着急,这里只是一个框架结构,让大家知道nginx是如何维护这些数据结构,如何对这些数据结构进行处理稍后将会分析。

 
  1. struct ngx_http_request_s

  2. {

  3. //这个指针只对原始请求有效,其它请求则会空。

  4. //如果是原始请求,则指向第一个子请求链表节点

  5. ngx_http_posted_request_t * posted_requests;

  6. }

  7. //子请求链表节点

  8. struct ngx_http_posted_request_s

  9. {

  10. ngx_http_request_t *request; //指向子请求

  11. ngx_http_posted_request_t *next; //指向下一个子请求

  12. };

看下nginx服务器是如何维护这个单链表的。http请求结构中有一个posted_requests指针,这个指针只对原始请求有效,其它的请求不会用到这个字段,也就是把posted_requests设置为null。而如果是原始请求,则会创接子请求链表。构成这样的一个单链表,后续有事件到时,可以调度所有的子请求进行处理,使得每一个子请求都有机会调度执行。

(3)子请求输出顺序

原始请求创建的各个子请求,以及子请求本身也可以创建子请求。那这些子请求处理完成后,总要把响应数据发给客户端浏览器吧!  在发送响应给客户端浏览器时,这些子请求、以及原始请求总有一个先后顺序。先后顺序的规则就是: 树的后序遍历操作的结果。以图: 树加链表结构为例来说明各个请求的输出响应数据给客户端浏览器的先后顺序。

sub2_1_data是sub2_1子请求产生的响应数据; sub2_2_data是sub2_2子请求产生的响应数据;    sub2_data则是sub2子请求产生的响应数据; r_data是原始请求产生的响应数据。这些都是叶子节点,可以直接输出数据。如果不好理解,可以换一种角度来理解。假设把这些请求产生的数据保存到子请求本身中,而不是重新创建一个节点并插入到postponed链表末尾,则输出顺序为:

不知这样有没更好理解子请求、以及原始请求的输出顺序? 子请求产生的数据节点与子请求本身节点对应关系如下:

二、子请求的创建

在对子请求有了整体的认识后,下面来看下nginx是如何创建子请求的。创建子请求的过程其实就是在创建前面介绍的树加链表结构,以及构成一个原始请求维护的单项链表结构。下面按场景来分析子请求的创建过程。

(1)创建子请求时,子请求会复用原始请求的成员。例如新创建的子请求会复用原始请求的包体缓冲区, http请求的版本号信息等、以及http请求的uri参数。

 
  1. //创建一个子请求,使用链表构造一颗树形结构

  2. //将子请求添加到原始请求的链表模块,这样原始请求可以知道所有子请求,包括孙子请求

  3. ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,

  4. ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,

  5. ngx_http_post_subrequest_t *ps, ngx_uint_t flags)

  6. {

  7. sr->request_body = r->request_body;

  8. //子请求方法只能是get

  9. sr->method = NGX_HTTP_GET;

  10. sr->http_version = r->http_version;

  11. sr->request_line = r->request_line;

  12. sr->uri = *uri;

  13. }

(2)创建子请求时,会设置子请求的读事件回调,以及写事件回调。因为子请求并不直接跟客户端交互,所有不需要处理子请求的读事件方法,因此读事件方法设置为不做任何事情的空函数。而写事件回调会被设为:ngx_http_handler, 这样在子请求被调度执行时,可以调用介入11个阶段的http模块进行处理。

 
  1. ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,

  2. ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,

  3. ngx_http_post_subrequest_t *ps, ngx_uint_t flags)

  4. {

  5. //指向父请求

  6. sr->parent = r;

  7. sr->post_subrequest = ps;

  8. //子请求不跟客户端交互,因此不需要读取客户端事件

  9. sr->read_event_handler = ngx_http_request_empty_handler;

  10. //调用各个http模块协同处理这个请求

  11. sr->write_event_handler = ngx_http_handler;

  12. }

(3)创建子请求时,将子请求添加到父请求的postponed链表中,构成一个树+链表组成的数据结构。

 
  1. ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,

  2. ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,

  3. ngx_http_post_subrequest_t *ps, ngx_uint_t flags)

  4. {

  5. pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));

  6. pr->request = sr;

  7. pr->out = NULL;

  8. pr->next = NULL;

  9. //将子请求添加到链表,构成一个树+链表结构

  10. if (r->postponed)

  11. {

  12. for (p = r->postponed; p->next; p = p->next)

  13. {

  14. }

  15. p->next = pr;

  16. }

  17. else

  18. {

  19. r->postponed = pr;

  20. }

  21. }

(4)最后将子请求加入到原始请求你的posted_requests链表中,这样原始请求就知道了所有的子请求,包括孙子请求。后续将会调用ngx_http_run_posted_requests遍历所有的子请求,调度各个子请求进行处理。

 
  1. //将子请求加入到原始请求链表的末尾。这样原始请求就知道了所有的子请求,包括孙子请求

  2. ngx_int_t ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)

  3. {

  4. ngx_http_posted_request_t **p;

  5. if (pr == NULL)

  6. {

  7. pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));

  8. }

  9. pr->request = r;

  10. pr->next = NULL;

  11. for (p = &r->main->posted_requests; *p; p = &(*p)->next)

  12. {

  13. }

  14. *p = pr;

  15. return NGX_OK;

  16. }

(5)有个疑问? 在创建子请求时,nginx服务器是怎么知道哪一个请求是最前面的请求,也就是最先发送响应数据给客户端的那个请求? 答案是nginx使用连接对象ngx_connection_s的data成员, 使用data成员指向最先发送响应数据给客户端的那个请求。 来看个例子,  假设原始请求开始的时候创建了两个子请求sub1,sub2, 则最先发送响应数据给客户端的是子请求sub1

而现在子请求sub1又创建了一个子请求sub1_1, 则此时最先发送响应数据给客户端的子请求为sub1_1。 也就是说此时c->data指向了sub1_1。

来看下代码的实现过程。原始请求创建子请求sub1, sub2时, c->data指向的是sub1这个子请求。而子请求本身又可以创建子请求,这是一个递归的过程。因此sub1这个子请求创建sub1_1子请求时, 此时c->data指向了sub1_1这个子请求。因此sub1_1这个子请求是可以最先给客户端发送响应数据的请求, 其它请求则需要缓存,等待sub1_1子请求结束。

 
  1. ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,

  2. ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,

  3. ngx_http_post_subrequest_t *ps, ngx_uint_t flags)

  4. {

  5. //复用data存放最前面的请求,这个请求的数据可以直接发送给客户端。其它的子请求需要缓存数据,等待

  6. //最前面的请求结束。如果r为子请求,r没有子请求了,且r为最前面的请求。则最前面的请求将会被切换为

  7. //这个刚刚创建的r的子请求

  8. if (c->data == r && r->postponed == NULL)

  9. {

  10. c->data = sr;

  11. }

  12. }

三、子请求的调度执行

在创建完子请求后,那这些子请求什么时候被调度执行呢? 当原始请求已经启动并且执行完成后,会查看是否有待处理的子请求,当然也包括孙子请求,有的话逐个调度这些子请求,开始子请求的处理。这些子请求,包括孙子请求都是原始请求的posted_requests单项链表中的一个节点。函数ngx_http_run_posted_requests会遍历这个单项链表,逐个调度各个子请求。当子请求调度执行后,会从单向链表中删除。如果后续子请求产生的数据被缓存了,还是会重新加回到这个单项链表中,这样可以再次触发这个子请求。

 
  1. static void ngx_http_process_request(ngx_http_request_t *r)

  2. {

  3. //调用各个http模块协同处理这个原始请求

  4. ngx_http_handler(r);

  5. //调度执行所有子请求

  6. ngx_http_run_posted_requests(c);

  7. }

 
  1. //处理子请求,处理完后将从队列中删除

  2. void ngx_http_run_posted_requests(ngx_connection_t *c)

  3. {

  4. //循环处理所有的子请求

  5. for ( ;; )

  6. {

  7. r = c->data;

  8. pr = r->main->posted_requests;

  9. //指向下一个子请求

  10. r->main->posted_requests = pr->next;

  11. r = pr->request;

  12. //在函数ngx_http_handler设置为ngx_http_core_run_phases

  13. r->write_event_handler(r);

  14. }

  15. }

那子请求被调度执行时,会做些什么呢? 在创建子请求时,已经把读事件回调设置为不做任何事件的ngx_http_request_empty_handler, 而把写事件回调设置为: ngx_http_handler。在子请求调度执行时,会调用这个函数进行处理。

 
  1. ngx_int_t ngx_http_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,

  2. ngx_http_post_subrequest_t *ps, ngx_uint_t flags)

  3. {

  4. sr->read_event_handler = ngx_http_request_empty_handler;//子请求不跟客户端交互,因此不需要读取客户端事件

  5. sr->write_event_handler = ngx_http_handler; //调用各个http模块协同处理这个请求

  6. }

现在看下ngx_http_handler这个函数做了些什么? 这个函数其实就是为了调度介入11个请求阶段的各个http模块进行处理。这和原始请求的处理过程是一样的。在前面的文章已经详细分析过了,如果不是很清楚可以参考nginx处理http请求这篇文章。

 
  1. void ngx_http_handler(ngx_http_request_t *r)

  2. {

  3. r->write_event_handler = ngx_http_core_run_phases;

  4. ngx_http_core_run_phases(r);

  5. }

  6. //调用各个http模块协同处理这个请求

  7. void ngx_http_core_run_phases(ngx_http_request_t *r)

  8. {

  9. ph = cmcf->phase_engine.handlers;

  10. //调用各个http模块的checker方法,使得各个http模块可以介入http请求

  11. while (ph[r->phase_handler].checker)

  12. {

  13. rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

  14. //返回NGX_OK,则会把控制全交由给事件模块

  15. if (rc == NGX_OK)

  16. {

  17. return;

  18. }

  19. }

  20. }

四、子请求的缓存

那什么情况下请求产生的数据需要缓存呢?  因为一个主请求可以创建多个子请求,这些子请求并行与后端服务器通信。但并一定先创建的子请求就一定会处理完与后端服务器的通信过程,因为这是一个异步过程。nginx服务器会记录哪个子请求是可以最先返回数据给客户端浏览器的,因此只要不是这个可以最先返回数据给客户端的子请求完成了与后端服务器的通信,这些子请求产生的数据都需要进行缓存。子请求处理完成时,调用过滤器模块提供的方法,把响应数据发送给客户端浏览器。但执行到ngx_http_postpone_filter_module过滤模块时,会判断是否需要对子请求产生的数据进行缓存。 ngx_http_postpone_filter函数就是负责缓存各个子请求产生的响应数据的,当然了,如果是最前面的请求,则会立马发送响应数据给客户端浏览器,而不会进行缓存。还是以图: 树加链表结构为例进行说明。

假设最开始时,主请求完成处理后,发送响应数据给客户端浏览器。而由于主请求不是最前面可以发送响应数据给客户端浏览器的请求,因此主请求产生的响应数据将会缓存。nginx做法是创建一个子节点r_data,用来存放原始请求产生的数据,并把这个子节点挂载到原始请求r的postponed链表的末尾。

现在假设子请求sub2_1也完成了处理,发送响应数据给客户端浏览器。而由于sub2_1不是最前面可以发送响应数据给客户端浏览器的子请求,因此该请求产生的响应数据将会缓存。同样的,nginx将会创建一个子节点sub2_1_data, 缓存sub2_1产生的响应数据,并加入到sub2_1子请求的postponed链表的末尾。

现在假设子请求sub2也完成了处理,发送响应数据给客户端浏览器。而由于sub2不是最前面可以发送响应数据给客户端浏览器的子请求,因此该请求产生的响应数据将会缓存。同样的,nginx将会创建一个子节点sub2_data, 缓存sub2产生的响应数据,并加入到sub2子请求的postponed链表的末尾。

现在假设子请求sub2_2也完成了处理,发送响应数据给客户端浏览器。而由于sub2_2不是最前面可以发送响应数据给客户端浏览器的子请求,因此该请求产生的响应数据将会缓存。同样的,nginx将会创建一个子节点sub2_2_data,缓存sub2_2产生的响应数据,并加入到sub2_2子请求的postponed链表的末尾。

最后,sub1子请求处理完成,发送响应数据给客户端浏览器。而由于sub1是最前面可以发送响应数据给客户端浏览器的子请求,因此ngx_http_postpone_filter函数会直接将sub1子请求产生的数据发送给客户端浏览器,而不会进行缓存。

如果理解了nginx服务器是如何缓存子请求产出的响应数据的过程,现在分析源码就简单多了。

 
  1. static ngx_int_t ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)

  2. {

  3. //当前请求不是最前面的请求,则需要把当前请求的数据缓存起来

  4. if (r != c->data)

  5. {

  6. if (in)

  7. {

  8. ngx_http_postpone_filter_add(r, in);

  9. return NGX_OK;

  10. }

  11. return NGX_OK;

  12. }

  13. //*********执行到这里,说明当前请求就是最前面的请求***********//

  14. //这个最前面的请求没有子请求了,也就是后续遍历结束,左孩子,右孩子都没有了。

  15. //可以把内容直接输出给客户端

  16. if (r->postponed == NULL)

  17. {

  18. if (in || c->buffered)

  19. {

  20. return ngx_http_next_filter(r->main, in);

  21. }

  22. return NGX_OK;

  23. }

  24. return NGX_OK;

  25. }

五、子请求结束处理

在最前面的子请求结束时,也就是最先可以发送响应数据给客户端浏览器的那个子请求。这个子请求结束时,那最新可以发送响应数据给客户端浏览器的子请求会切换成哪一个子请求呢? 还是以图: 树加链表结构为例进行说明。

开始时sub1是最先可以发送响应数据给客户端浏览器的子请求。这个子请求结束时,会将c->data指向原始请求r, 此时原始请求就变成了最先可以发送响应数据给客户端浏览器的请求。

原始请求变成了最先可以发送响应数据给客户端浏览器的请求。但原始请求不是叶子节点,因此需要递归的在原始请求的子请求链表中继续查找最先可以发送响应数据给客户端浏览器的请求。此时原始请求r的第一子请求sub2就变成了最先可以发送响应数据给客户端浏览器的请求。其实这也就是对一颗树进行后续遍历的操作。

sub2子请求原始请求变成了最先可以发送响应数据给客户端浏览器的请求。但sub2子请求也不是叶子节点,因此需要递归的在sub2请求的子请求链表中继续查找最先可以发送响应数据给客户端浏览器的请求。此时sub2的第一个子请求sub2_1就变成了最先可以发送响应数据给客户端浏览器的请求。其实这也就是对以sub2为根节点的树进行后续遍历的操作。

sub2_1子请求变成了最先可以发送响应数据给客户端浏览器的请求。但sub2_1子请求仍然不是叶子节点,因此需要递归的在sub2_1请求的子请求链表中继续查找最先可以发送响应数据给客户端浏览器的请求。其实这也就是对以sub2_1为根节点的树进行后续遍历的操作。此时sub2_1的第一子请求sub2_1_data就变成了最先可以发送响应数据给客户端浏览器的请求。而这个sub2_1_data是一个叶子节点了,存放了sub2_1子请求的响应数据。因此sub2_1_data是最先可以发送响应数据给客户端浏览器的请求,可以直接把响应数据发给客户端浏览器。

理解了最前面的子请求结束后,如何查找到下一个最先可以发送响应数据给客户端浏览器的子请求的处理过程,现在分析代码就简单多了。

 
  1. //由各个http模块调用的,释放http请求的函数

  2. void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

  3. {

  4. //说明是子请求

  5. if (r != r->main)

  6. {

  7. //执行到这里,说明子请求的所有数据都发送完成了

  8. pr = r->parent;

  9. if (r == c->data)

  10. {

  11. r->done = 1;

  12. //postponed链表头指向下一个节点

  13. if (pr->postponed && pr->postponed->request == r)

  14. {

  15. pr->postponed = pr->postponed->next;

  16. }

  17. //父请求设置为最前面的请求,也就是可以最先发送响应数据给客户端的请求

  18. c->data = pr;

  19. }

  20. //将子请求的父请求重新添加到链表,目的是为了使得请求再次被调度执行

  21. ngx_http_post_request(pr, NULL);

  22. return;

  23. }

  24. }

说白了ngx_http_finalize_request处理的事情就是在子请求结束时,将最先可以发送响应数据给客户端浏览器的子请求设置为父请求。而ngx_http_postpone_filter函数中的这个do while循环就是为了递归查找到叶子节点,使得这个叶子节点成为最先可以发送响应数据给客户端浏览器的子请求。nginx执行这两个操作,就可以查找到最先发送响应数据给客户端浏览器的子请求。这两个操作时密切配合的。

 
  1. static ngx_int_t ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)

  2. {

  3. do

  4. {

  5. pr = r->postponed;

  6. //pr还有子请求,则下面需要查找到最前面的请求

  7. if (pr->request)

  8. {

  9. r->postponed = pr->next;

  10. c->data = pr->request;

  11. //将这个阶段重新加入到原始请求的子请求链表中。为什么要这么做呢?因为子请求数据已经产生了,子请求

  12. //调度执行时已经从原始请求的子请求链表中删除。这样这个子请求就不会被调度执行了。

  13. return ngx_http_post_request(pr->request, NULL);

  14. }

  15. //说明pr是一个数据节点,没有子请求了,是一个叶子节点,可以直接输出数据

  16. if (pr->out == NULL)

  17. {

  18. }

  19. else

  20. {

  21. if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR)

  22. {

  23. return NGX_ERROR;

  24. }

  25. }

  26. r->postponed = pr->next;

  27. } while (r->postponed);

  28. }

到此为止,子请求的并发处理已经分析完了。现在做个总结,对于子请求的并发处理需要掌握以下几个内容:

(1) nginx为了支持子请求功能使用了树+链表的数据结构,以及在原始请求中使用了一个单项链表post_requests维护了所有的子请求,包括孙子请求。

(2) 子请求是如何创建的,创建过程做了哪些事情。在创建过程是如何找到最先可以发送响应数据给客户端浏览器的子请求。

(3) nginx是如何调度各个子请求进行处理的。

(4) nginx子请求处理完成后是如何进行缓存的。

(5) 最前面的请求结束后,又是如何找到最先可以发送响应数据给客户端浏览器的子请求。

(6) nginx的子请求被调度执行时,会从原始请求的post_requests单项链表中移除,那为什么有些情况,这个被移除的子请求会再次插入到原始请求的post_requests单项链表的末尾呢?  这是为了使得这个子请求再次被http框架调度执行,将剩余的响应数据发送给客户端浏览器。

--------------------- 本文来自 ApeLife 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/apelife/article/details/75003346?utm_source=copy

nginx子请求并发处理相关推荐

  1. nginx 子请求接收响应_Nginx详解其原理

    Nginx进程模型分析  在介绍Nginx的进程模型之前我们先来给大家解释下一些常见的名词,这能辅助我们更好的了解Nginx的进程模型.作为Web服务器,设计的初衷就是为了能够处理更多的客户端的请求, ...

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

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

  3. Nginx HTTP请求的11个阶段

    前言 上一篇文章(https://blog.csdn.net/zzhongcy/article/details/86086369)简单说明了nginx的11个阶段, 今天网上看到一篇详细介绍的文章,这 ...

  4. 第一次使用MsChart运行提示:ChartImg.axd 执行子请求时出错。

    第一次尝试使用MsChart提示:ChartImg.axd 执行子请求时出错. 解决方法,在web.config种做如下调整: <appSettings>     <add key= ...

  5. Nginx处理请求的11个阶段

    Nginx 处理请求的全过程一共划分为 11 个阶段(如图),按阶段由上到下依次执行 (上一阶段的所有指令执行完毕,才进入下一阶段) 各阶段的含义如下: ² post-read: 接收到完整的http ...

  6. nginx实现请求的负载均衡 + keepalived实现nginx的高可用

    前言 使用集群是网站解决高并发.海量数据问题的常用手段.当一台服务器的处理能力.存储空间不足时,不要企图去换更强大的服务器,对大型网站而言,不管多么强大的服务器,都满足不了网站持续增长的业务需求.这种 ...

  7. nginx限制请求之四:目录进行IP限制

    相关文章: <高可用服务设计之二:Rate limiting 限流与降级> <nginx限制请求之一:(ngx_http_limit_conn_module)模块> <n ...

  8. nginx的请求接收流程(二)

    在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,heade ...

  9. nginx post请求超时_nginx记录分析网站响应慢的请求(ngx_http_log_request_speed)

    nginx模块ngx_http_log_request_speed可以用来找出网站哪些请求很慢,针对站点很多,文件以及请求很多想找出哪些请求比较慢的话,这个插件非常有效.作者的初衷是写给自己用的,用来 ...

最新文章

  1. 隐马尔科夫模型HMM(一)HMM模型
  2. web项目导入变java项目_eclipse导入web项目变成java项目解决办法
  3. UA MATH523A 实分析1 集合论基础7 一些度量空间基本概念
  4. 13.跨进程读写内存
  5. 【Redis学习】Redis持久化
  6. Linux下V4L2编程小结
  7. mete30是鸿蒙系统么,华为mete30pro什么时候能用上鸿蒙系统?
  8. 汤阳光 Hibernate笔记
  9. 题目:[NOIP 2011]计算系数
  10. 【干货】如何判断 Sql 注入点
  11. 《语音信号处理》 语音识别章节 读书笔记
  12. 科技爱好者周刊:第 61 期
  13. 后盾网php项目开发实例,后盾网thinkphp许愿墙html源码
  14. MindManager22直装版思维导图新功能
  15. spyder/conda安装包报错:conda info could not be constructed. KeyError: ‘pkgs_dirs‘
  16. 论文翻译:2019_Bandwidth Extension On Raw Audio Via Generative Adversarial Networks
  17. 血氧仪电路方案开发--PCBA方案
  18. openCV中二值化图像的方法
  19. NBA勇士陷入苦境 威金斯不保证这季会归队
  20. 【计算机毕业设计】二手车交易网站

热门文章

  1. 每日一皮:这真的是亲爹吗...
  2. 全国第九大电商平台倒了!创始人卷走260亿,1200万人被骗
  3. 每日一皮:客户被绑,蒙眼,惊问 “想干什么?”
  4. ThreadLocal 和神奇的数字 0x61c88647
  5. 自律到极致-人生才精致:第12期
  6. 如何模拟将CPU、IO打满?
  7. 月薪8k 和 月薪28K的程序员差距在哪里?
  8. 华为SAN存储在linux下的,多路径 - 华为SAN存储在SUSE系统下的主机连通性指南 - 华为...
  9. python向mysql中添加数据标签_用python在MySQL中写入数据和添加数据
  10. JDK8特性--Stream(list转map)