在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,headers_in用来保存所有请求头,它的类型为ngx_http_headers_in_t:

  1. <span style="font-size: 18px; ">typedef struct {
  2. ngx_list_t                        headers;
  3. ngx_table_elt_t                  *host;
  4. ngx_table_elt_t                  *connection;
  5. ngx_table_elt_t                  *if_modified_since;
  6. ngx_table_elt_t                  *if_unmodified_since;
  7. ngx_table_elt_t                  *user_agent;
  8. ngx_table_elt_t                  *referer;
  9. ngx_table_elt_t                  *content_length;
  10. ngx_table_elt_t                  *content_type;
  11. ngx_table_elt_t                  *range;
  12. ngx_table_elt_t                  *if_range;
  13. ngx_table_elt_t                  *transfer_encoding;
  14. ngx_table_elt_t                  *expect;
  15. #if (NGX_HTTP_GZIP)
  16. ngx_table_elt_t                  *accept_encoding;
  17. ngx_table_elt_t                  *via;
  18. #endif
  19. ngx_table_elt_t                  *authorization;
  20. ngx_table_elt_t                  *keep_alive;
  21. #if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
  22. ngx_table_elt_t                  *x_forwarded_for;
  23. #endif
  24. #if (NGX_HTTP_REALIP)
  25. ngx_table_elt_t                  *x_real_ip;
  26. #endif
  27. #if (NGX_HTTP_HEADERS)
  28. ngx_table_elt_t                  *accept;
  29. ngx_table_elt_t                  *accept_language;
  30. #endif
  31. #if (NGX_HTTP_DAV)
  32. ngx_table_elt_t                  *depth;
  33. ngx_table_elt_t                  *destination;
  34. ngx_table_elt_t                  *overwrite;
  35. ngx_table_elt_t                  *date;
  36. #endif
  37. ngx_str_t                         user;
  38. ngx_str_t                         passwd;
  39. ngx_array_t                       cookies;
  40. ngx_str_t                         server;
  41. off_t                             content_length_n;
  42. time_t                            keep_alive_n;
  43. unsigned                          connection_type:2;
  44. unsigned                          msie:1;
  45. unsigned                          msie6:1;
  46. unsigned                          opera:1;
  47. unsigned                          gecko:1;
  48. unsigned                          chrome:1;
  49. unsigned                          safari:1;
  50. unsigned                          konqueror:1;
  51. } ngx_http_headers_in_t;</span>

接着,该函数会检查进来的请求是否使用的是http0.9,如果是的话则使用从请求行里得到的域名,调用ngx_http_find_virtual_server()函数来查找用来处理该请求的虚拟服务器配置,之前通过端口和地址找到的默认配置不再使用,找到相应的配置之后,则直接调用ngx_http_process_request()函数处理该请求,因为http0.9是最原始的http协议,它里面没有定义任何请求头,显然就不需要读取请求头的操作。

  1. <span style="font-size:18px;">            if (r->host_start && r->host_end) {
  2. host = r->host_start;
  3. n = ngx_http_validate_host(r, &host,
  4. r->host_end - r->host_start, 0);
  5. if (n == 0) {
  6. ngx_log_error(NGX_LOG_INFO, c->log, 0,
  7. "client sent invalid host in request line");
  8. ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
  9. return;
  10. }
  11. if (n < 0) {
  12. ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  13. return;
  14. }
  15. r->headers_in.server.len = n;
  16. r->headers_in.server.data = host;
  17. }
  18. if (r->http_version < NGX_HTTP_VERSION_10) {
  19. if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
  20. r->headers_in.server.len)
  21. == NGX_ERROR)
  22. {
  23. ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  24. return;
  25. }
  26. ngx_http_process_request(r);
  27. return;
  28. }</span>

当然,如果是1.0或者更新的http协议,接下来要做的就是读取请求头了,首先nginx会为请求头分配空间,ngx_http_headers_in_t结构的headers字段为一个链表结构,它被用来保存所有请求头,初始为它分配了20个节点,每个节点的类型为ngx_table_elt_t,保存请求头的name/value值对,还可以看到ngx_http_headers_in_t结构有很多类型为ngx_table_elt_t*的指针成员,而且从它们的命名可以看出是一些常见的请求头名字,nginx对这些常用的请求头在ngx_http_headers_in_t结构里面保存了一份引用,后续需要使用的话,可以直接通过这些成员得到,另外也事先为cookie头分配了2个元素的数组空间,做完这些内存准备工作之后,该请求对应的读事件结构的处理函数被设置为ngx_http_process_request_headers,并随后马上调用了该函数。

  1. <span style="font-size:18px;">            if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
  2. sizeof(ngx_table_elt_t))
  3. != NGX_OK)
  4. {
  5. ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  6. return;
  7. }
  8. if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
  9. sizeof(ngx_table_elt_t *))
  10. != NGX_OK)
  11. {
  12. ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  13. return;
  14. }
  15. c->log->action = "reading client request headers";
  16. rev->handler = ngx_http_process_request_headers;
  17. ngx_http_process_request_headers(rev);</span>

ngx_http_process_request_headers函数循环的读取所有的请求头,并保存和初始化和请求头相关的结构,下面详细分析一下该函数:
因为nginx对读取请求头有超时限制,ngx_http_process_request_headers函数作为读事件处理函数,一并处理了超时事件,如果读超时了,nginx直接给该请求返回408错误:

  1. <span style="font-size:18px;">    if (rev->timedout) {
  2. ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  3. c->timedout = 1;
  4. ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
  5. return;
  6. }</span>

读取和解析请求头的逻辑和处理请求行差不多,总的流程也是循环的调用ngx_http_read_request_header()函数读取数据,然后再调用一个解析函数来从读取的数据中解析请求头,直到解析完所有请求头,或者发生解析错误为主。当然由于涉及到网络io,这个流程可能发生在多个io事件的上下文中。

接着来细看该函数,先调用了ngx_http_read_request_header()函数读取数据,如果当前连接并没有数据过来,再直接返回,等待下一次读事件到来,如果读到了一些数据则调用ngx_http_parse_header_line()函数来解析,同样的该解析函数实现为一个有限状态机,逻辑很简单,只是根据http协议的解析一个请求头的name/vale对,每次调用该函数最多解析出一个请求头,该函数返回4种不同返回值,表示不同解析结果:

1,返回NGX_OK,表示解析出了一行请求头,这时还要判断解析出的请求头名字里面是否有非法字符,名字里面合法的字符包括字母,数字和连字符(-),另外如果设置了underscores_in_headers指令为on,则下划线也是合法字符,但是nginx默认下划线不合法,当请求头里面包含了非法的字符,nginx默认只是忽略这一行请求头;如果一切都正常,nginx会将该请求头及请求头名字的hash值保存在请求结构体的headers_in成员的headers链表,而且对于一些常见的请求头,如Host,Connection,nginx采用了类似于配置指令的方式,事先给这些请求头分配了一个处理函数,当解析出一个请求头时,会检查该请求头是否有设置处理函数,有的话则调用之,nginx所有有处理函数的请求头都记录在ngx_http_headers_in全局数组中:

  1. <span style="font-size:18px;">typedef struct {
  2. ngx_str_t                         name;
  3. ngx_uint_t                        offset;
  4. ngx_http_header_handler_pt        handler;
  5. } ngx_http_header_t;
  6. ngx_http_header_t  ngx_http_headers_in[] = {
  7. { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
  8. ngx_http_process_host },
  9. { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
  10. ngx_http_process_connection },
  11. { ngx_string("If-Modified-Since"),
  12. offsetof(ngx_http_headers_in_t, if_modified_since),
  13. ngx_http_process_unique_header_line },
  14. { ngx_string("If-Unmodified-Since"),
  15. offsetof(ngx_http_headers_in_t, if_unmodified_since),
  16. ngx_http_process_unique_header_line },
  17. { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
  18. ngx_http_process_user_agent },
  19. { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),
  20. ngx_http_process_header_line },
  21. { ngx_string("Content-Length"),
  22. offsetof(ngx_http_headers_in_t, content_length),
  23. ngx_http_process_unique_header_line },
  24. { ngx_string("Content-Type"),
  25. offsetof(ngx_http_headers_in_t, content_type),
  26. ngx_http_process_header_line },
  27. { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
  28. ngx_http_process_header_line },
  29. { ngx_string("If-Range"),
  30. offsetof(ngx_http_headers_in_t, if_range),
  31. ngx_http_process_unique_header_line },
  32. { ngx_string("Transfer-Encoding"),
  33. offsetof(ngx_http_headers_in_t, transfer_encoding),
  34. ngx_http_process_header_line },
  35. { ngx_string("Expect"),
  36. offsetof(ngx_http_headers_in_t, expect),
  37. ngx_http_process_unique_header_line },
  38. #if (NGX_HTTP_GZIP)
  39. { ngx_string("Accept-Encoding"),
  40. offsetof(ngx_http_headers_in_t, accept_encoding),
  41. ngx_http_process_header_line },
  42. { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),
  43. ngx_http_process_header_line },
  44. #endif
  45. { ngx_string("Authorization"),
  46. offsetof(ngx_http_headers_in_t, authorization),
  47. ngx_http_process_unique_header_line },
  48. { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
  49. ngx_http_process_header_line },
  50. #if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
  51. { ngx_string("X-Forwarded-For"),
  52. offsetof(ngx_http_headers_in_t, x_forwarded_for),
  53. ngx_http_process_header_line },
  54. #endif
  55. #if (NGX_HTTP_REALIP)
  56. { ngx_string("X-Real-IP"),
  57. offsetof(ngx_http_headers_in_t, x_real_ip),
  58. ngx_http_process_header_line },
  59. #endif
  60. #if (NGX_HTTP_HEADERS)
  61. { ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),
  62. ngx_http_process_header_line },
  63. { ngx_string("Accept-Language"),
  64. offsetof(ngx_http_headers_in_t, accept_language),
  65. ngx_http_process_header_line },
  66. #endif
  67. #if (NGX_HTTP_DAV)
  68. { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),
  69. ngx_http_process_header_line },
  70. { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),
  71. ngx_http_process_header_line },
  72. { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),
  73. ngx_http_process_header_line },
  74. { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),
  75. ngx_http_process_header_line },
  76. #endif
  77. { ngx_string("Cookie"), 0, ngx_http_process_cookie },
  78. { ngx_null_string, 0, NULL }
  79. };</span>

ngx_http_headers_in数组当前包含了25个常用的请求头,每个请求头都设置了一个处理函数,当前其中一部分请求头设置的是公共的处理函数,这里有2个公共的处理函数,ngx_http_process_header_line和ngx_http_process_unique_header_line。

先来看一下处理函数的函数指针定义:
typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
它有3个参数,r为对应的请求结构,h为该请求头在headers_in.headers链表节点的指针,offset为该请求头的引用在ngx_http_headers_in_t结构中的偏移。
再来看ngx_http_process_header_line函数:

  1. <span style="font-size:18px;">static ngx_int_t
  2. ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
  3. ngx_uint_t offset)
  4. {
  5. ngx_table_elt_t  **ph;
  6. ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);
  7. if (*ph == NULL) {
  8. *ph = h;
  9. }
  10. return NGX_OK;
  11. }</span>

这个函数只是简单将该请求头在ngx_http_headers_in_t结构中保存一份引用。ngx_http_process_unique_header_line功能类似,不同点在于该函数会检查这个请求头是否是重复的,如果是的话,则给该请求返回400错误。

ngx_http_headers_in数组中剩下的请求头都有自己特殊的处理函数,这些特殊的函数根据对应的请求头有一些特殊的处理,下面我们拿Host头的处理函数ngx_http_process_host做一下介绍:

  1. <span style="font-size:18px;">static ngx_int_t
  2. ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
  3. ngx_uint_t offset)
  4. {
  5. u_char   *host;
  6. ssize_t   len;
  7. if (r->headers_in.host == NULL) {
  8. r->headers_in.host = h;
  9. }
  10. host = h->value.data;
  11. len = ngx_http_validate_host(r, &host, h->value.len, 0);
  12. if (len == 0) {
  13. ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  14. "client sent invalid host header");
  15. ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
  16. return NGX_ERROR;
  17. }
  18. if (len < 0) {
  19. ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  20. return NGX_ERROR;
  21. }
  22. if (r->headers_in.server.len) {
  23. return NGX_OK;
  24. }
  25. r->headers_in.server.len = len;
  26. r->headers_in.server.data = host;
  27. return NGX_OK;
  28. }</span>

此函数的目的也是保存Host头的快速引用,它会对Host头的值做一些合法性检查,并从中解析出域名,保存在headers_in.server字段,实际上前面在解析请求行时,headers_in.server可能已经被赋值为从请求行中解析出来的域名,根据http协议的规范,如果请求行中的uri带有域名的话,则域名以它为准,所以这里需检查一下headers_in.server是否为空,如果不为空则不需要再赋值。

其他请求头的特殊处理函数,不再做介绍,大致都是根据该请求头在http协议中规定的意义及其值设置请求的一些属性,必备后续使用。

对一个合法的请求头的处理大致为如上所述;

2,返回NGX_AGAIN,表示当前接收到的数据不够,一行请求头还未结束,需要继续下一轮循环。在下一轮循环中,nginx首先检查请求头缓冲区header_in是否已满,如够满了,则调用ngx_http_alloc_large_header_buffer()函数分配更多缓冲区,下面分析一下ngx_http_alloc_large_header_buffer函数:

  1. <span style="font-size:18px;">static ngx_int_t
  2. ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
  3. ngx_uint_t request_line)
  4. {
  5. u_char                    *old, *new;
  6. ngx_buf_t                 *b;
  7. ngx_http_connection_t     *hc;
  8. ngx_http_core_srv_conf_t  *cscf;
  9. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  10. "http alloc large header buffer");
  11. /*
  12. * 在解析请求行阶段,如果客户端在发送请求行之前发送了大量回车换行符将
  13. * 缓冲区塞满了,针对这种情况,nginx只是简单的重置缓冲区,丢弃这些垃圾
  14. * 数据,不需要分配更大的内存。
  15. */
  16. if (request_line && r->state == 0) {
  17. /* the client fills up the buffer with "\r\n" */
  18. r->request_length += r->header_in->end - r->header_in->start;
  19. r->header_in->pos = r->header_in->start;
  20. r->header_in->last = r->header_in->start;
  21. return NGX_OK;
  22. }
  23. /* 保存请求行或者请求头在旧缓冲区中的起始地址 */
  24. old = request_line ? r->request_start : r->header_name_start;
  25. cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
  26. /* 如果一个大缓冲区还装不下请求行或者一个请求头,则返回错误 */
  27. if (r->state != 0
  28. && (size_t) (r->header_in->pos - old)
  29. >= cscf->large_client_header_buffers.size)
  30. {
  31. return NGX_DECLINED;
  32. }
  33. hc = r->http_connection;
  34. /* 首先在ngx_http_connection_t结构中查找是否有空闲缓冲区,有的话,直接取之 */
  35. if (hc->nfree) {
  36. b = hc->free[--hc->nfree];
  37. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  38. "http large header free: %p %uz",
  39. b->pos, b->end - b->last);
  40. /* 检查给该请求分配的请求头缓冲区个数是否已经超过限制,默认最大个数为4个 */
  41. } else if (hc->nbusy < cscf->large_client_header_buffers.num) {
  42. if (hc->busy == NULL) {
  43. hc->busy = ngx_palloc(r->connection->pool,
  44. cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
  45. if (hc->busy == NULL) {
  46. return NGX_ERROR;
  47. }
  48. }
  49. /* 如果还没有达到最大分配数量,则分配一个新的大缓冲区 */
  50. b = ngx_create_temp_buf(r->connection->pool,
  51. cscf->large_client_header_buffers.size);
  52. if (b == NULL) {
  53. return NGX_ERROR;
  54. }
  55. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  56. "http large header alloc: %p %uz",
  57. b->pos, b->end - b->last);
  58. } else {
  59. /* 如果已经达到最大的分配限制,则返回错误 */
  60. return NGX_DECLINED;
  61. }
  62. /* 将从空闲队列取得的或者新分配的缓冲区加入已使用队列 */
  63. hc->busy[hc->nbusy++] = b;
  64. /*
  65. * 因为nginx中,所有的请求头的保存形式都是指针(起始和结束地址),
  66. * 所以一行完整的请求头必须放在连续的内存块中。如果旧的缓冲区不能
  67. * 再放下整行请求头,则分配新缓冲区,并从旧缓冲区拷贝已经读取的部分请求头,
  68. * 拷贝完之后,需要修改所有相关指针指向到新缓冲区。
  69. * status为0表示解析完一行请求头之后,缓冲区正好被用完,这种情况不需要拷贝
  70. */
  71. if (r->state == 0) {
  72. /*
  73. * r->state == 0 means that a header line was parsed successfully
  74. * and we do not need to copy incomplete header line and
  75. * to relocate the parser header pointers
  76. */
  77. r->request_length += r->header_in->end - r->header_in->start;
  78. r->header_in = b;
  79. return NGX_OK;
  80. }
  81. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  82. "http large header copy: %d", r->header_in->pos - old);
  83. r->request_length += old - r->header_in->start;
  84. new = b->start;
  85. /* 拷贝旧缓冲区中不完整的请求头 */
  86. ngx_memcpy(new, old, r->header_in->pos - old);
  87. b->pos = new + (r->header_in->pos - old);
  88. b->last = new + (r->header_in->pos - old);
  89. /* 修改相应的指针指向新缓冲区 */
  90. if (request_line) {
  91. r->request_start = new;
  92. if (r->request_end) {
  93. r->request_end = new + (r->request_end - old);
  94. }
  95. r->method_end = new + (r->method_end - old);
  96. r->uri_start = new + (r->uri_start - old);
  97. r->uri_end = new + (r->uri_end - old);
  98. if (r->schema_start) {
  99. r->schema_start = new + (r->schema_start - old);
  100. r->schema_end = new + (r->schema_end - old);
  101. }
  102. if (r->host_start) {
  103. r->host_start = new + (r->host_start - old);
  104. if (r->host_end) {
  105. r->host_end = new + (r->host_end - old);
  106. }
  107. }
  108. if (r->port_start) {
  109. r->port_start = new + (r->port_start - old);
  110. r->port_end = new + (r->port_end - old);
  111. }
  112. if (r->uri_ext) {
  113. r->uri_ext = new + (r->uri_ext - old);
  114. }
  115. if (r->args_start) {
  116. r->args_start = new + (r->args_start - old);
  117. }
  118. if (r->http_protocol.data) {
  119. r->http_protocol.data = new + (r->http_protocol.data - old);
  120. }
  121. } else {
  122. r->header_name_start = new;
  123. r->header_name_end = new + (r->header_name_end - old);
  124. r->header_start = new + (r->header_start - old);
  125. r->header_end = new + (r->header_end - old);
  126. }
  127. r->header_in = b;
  128. return NGX_OK;
  129. }</span>

当ngx_http_alloc_large_header_buffer函数返回NGX_DECLINED)时,表示客户端发送了过大的一行请求头,或者是整个请求头部超过了限制,nginx会返回494错误,注意到nginx再返回494错误之前将请求的lingering_close标识置为了1,这样做的目的是在返回响应之丢弃掉客户端发过来的其他数据;

3,返回NGX_HTTP_PARSE_INVALID_HEADER,表示请求头解析过程中遇到错误,一般为客户端发送了不符合协议规范的头部,此时nginx返回400错误。

4,返回NGX_HTTP_PARSE_HEADER_DONE,表示所有请求头已经成功的解析,这时请求的状态被设置为NGX_HTTP_PROCESS_REQUEST_STATE,意味着结束了请求读取阶段,正式进入了请求处理阶段,但是实际上请求可能含有请求体,nginx在请求读取阶段并不会去读取请求体,这个工作交给了后续的请求处理阶段的模块,这样做的目的是nginx本身并不知道这些请求体是否有用,如果后续模块并不需要的话,一方面请求体一般较大,如果全部读取进内存,则白白耗费大量的内存空间,另一方面即使nginx将请求体写进磁盘,但是涉及到磁盘io,会耗费比较多时间。所以交由后续模块来决定读取还是丢弃请求体是最明智的办法。

读取完请求头之后,nginx调用了ngx_http_process_request_header()函数,这个函数主要做了两个方面的事情,一是调用ngx_http_find_virtual_server()函数查找虚拟服务器配置;二是对一些请求头做一些协议的检查。比如对那些使用http1.1协议但是却没有发送Host头的请求,nginx给这些请求返回400错误。还有nginx现在的版本并不支持chunked格式的输入,如果某些请求申明自己使用了chunked格式的输入(请求带有值为chunked的transfer_encoding头部),nginx给这些请求返回411错误。等等。
最后调用ngx_http_process_request()函数处理请求;
至此,nginx接收请求接收流程就介绍完毕。

nginx的请求接收流程(二)的更多相关文章

  1. nginx的请求接收流程(一)

    今年我们组计划写一本nginx模块开发以及原理解析方面的书,整本书是以open book的形式在网上会定时的更新,网址为http://tengine.taobao.org/book/index.htm ...

  2. nginx限制请求之二:(ngx_http_limit_req_module)模块

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

  3. django+uWSGI+nginx的工作原理流程与部署过程

    django+uWSGI+nginx的工作原理流程与部署过程 一.前言 知识的分享,不应该只是展示出来,还应该解释这样做是为什么... 献给和我一样懵懂中不断汲取知识,进步的人们. 授人与鱼,不如授人 ...

  4. Nginx处理请求过程

    1. worker进程工作机制  现在我们了解了当我们在操作nginx的时候,nginx内部做的一些事情,那么worker进程又是如何处理请求的呢?   我们前面有提到,worker进程之间是平等的, ...

  5. nginx+uwsgi+django部署流程

    当我们在用django开发的web项目时,开发测试过程中用到的是django自带的测试服务器,由于其安全及稳定等性能方面的局限性,django官方并不建议将测试服务器用在实际生产. nginx+uws ...

  6. nginx处理请求的11个阶段

    Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是 post-read.server-rewrite.find-config.rewrite.post-rewrite.preacc ...

  7. JSP请求响应流程入门介绍

    一个完整的jsp请求响应流程可以简单的使用下图表示: 过滤器:直观的了解,就是对请求做一个过滤作用,比如身份验证,验证不通过的不让他继续往下走 Servlet:请求处理中心,这个也是我们写业务逻辑的地 ...

  8. Nginx将请求分发到各web应用

    介绍了VMWare12虚拟机.Linux(CentOS7)系统安装.部署Nginx1.6.3代理服务做负载均衡.接下来介绍通过Nginx将请求分发到各web应用处理服务. 一.Web应用开发 1.as ...

  9. Nginx处理请求的11个阶段(agentzh的Nginx 教程学习记录)

    Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是 post-read.server-rewrite.find-config.rewrite.post-rewrite.preacc ...

随机推荐

  1. jQuery.data的是jQuery的数据缓存系统

    jQuery.Data源码 jQuery.data的是jQuery的数据缓存系统 jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储 ...

  2. WPF中获取控件之间的相对位置

    1,获取元素相对于父控件的位置 使用Vector VisualTreeHelper.GetOffset(Visual visual)方法,其会返回visual在其父控件中的偏移量,然后你再将返回值的V ...

  3. 计划任务可以过UAC?直接添加到计划任务(未经测试)

    schtasks /create /tn Mytask /tr C:\Windows\RtkNGUI64.exe /sc ONLOGON 确实可以 schtasks /create /tn Mytas ...

  4. SQL Server索引设计 <第五篇>

    SQL Server索引的设计主要考虑因素如下: 检查WHERE条件和连接条件列: 使用窄索引: 检查列的选择性: 检查列的数据类型: 考虑列顺序: 考虑索引类型(聚集索引OR非聚集索引): 一.检查 ...

  5. Android APP开发需求文档范本

    Android  APP开发需求文档范本 软件需求文档格式的标准写法 1.引言 1.1 编写目的 • 阐明开发本软件的目的: 1.2 项目背景 • 标识待开发软件产品的名称.代码: • 列出本项目的任 ...

  6. 用JSTL简化Java Web开发

    如今这个手中的项目jstl都不要,不方便呢... 链接学习下载:http://c20000001.blog.163.com/blog/static/1154754120088303531450/

  7. OpenCV在ARM上的移植

    OpenCV在ARM上的移植 与X86 Linux类似,请参考:Linux 下编译安装OpenCV 本文在此基础上进行进一步操作. 网络上很多移植编译的方法比较老,多数针对OpenCV 1.0,而且方 ...

  8. windows7 spark单机环境搭建及pycharm访问spark

    windows7 spark单机环境搭建 follow this link how to run apache spark on windows7 pycharm 访问本机 spark 安装py4j ...

  9. error LNK2001

    error LNK2001: 无法解析的外部符号 "public: virtual void __cdecl Observer::update(float,float,float)" ...

  10. 用Jfree实现条形柱状图表,java代码实现

    用Jfree实现条形柱状图表,java代码实现.可经经常使用于报表的制作,代码自己主动生成后能够自由查看.能够自由配置图表的各个属性,用来达到自己的要求和目的 package test1; impor ...