nginx http模块11个阶段驱动模式详解

原创
2020/04/07 08:43
阅读数 1.7K

        在nginx调用ngx_http_process_request_headers()方法读取完所有的header数据之后,就调用ngx_http_process_request()方法开始了请求的处理过程,这也就是nginx http模块开始处理请求的11个阶段的入口,本文则主要讲解nginx是如驱动http模块的11个阶段。

1. 处理入口ngx_http_process_request()

        关于ngx_http_process_request()方法,这里首先有两点需要读者理解的:

  • nginx是在接收完header数据之后就开始进入http模块的11个阶段进行处理的,这主要是因为,相对于多变的body数据,nginx更关注如何控制header数据的处理;
  • ngx_http_process_request()方法只是一个入口方法,最终这个方法将会进入执行http模块11个阶段的调用中,而这个部分是会读取请求的body数据的。由于TCP是流式数据,也就是说一次接收并不一定能够完整的接收所有数据,而且http模块的11个阶段中有部分阶段也可能会要求再次进行当前阶段的调用,这就还是回归到事件框架中了,这个时候会把事件的回调函数设置为ngx_http_request_handler()方法,根据请求需要,其是可以不断的将控制权交给该方法,以进行再次调用。也就是说这里的ngx_http_process_request()方法是一个简单的入口方法,而其内部的事件循环驱动的则是ngx_http_request_handler()方法。

        这里我们首先看一下ngx_http_process_request()方法的源码:

void ngx_http_process_request(ngx_http_request_t *r) {
  ngx_connection_t *c = r->connection;
  if (c->read->timer_set) {
    ngx_del_timer(c->read);
  }
  
  // 这里的ngx_http_request_handler()方法内部本质上就是根据事件的类型将当前事件委托给
  // r->read_event_handler或者r->write_event_handler方法进行处理
  c->read->handler = ngx_http_request_handler;
  c->write->handler = ngx_http_request_handler;
  // 这里将read_event_handler()方法设置为ngx_http_block_reading(),该方法只是将当前的读事件从事件
  // 循环中移除了,本质上其是没有做任何事情的,可以理解为读事件的一个空回调方法
  r->read_event_handler = ngx_http_block_reading;
  // 依次调用http请求的11个阶段处理请求
  ngx_http_handler(r);
  ngx_http_run_posted_requests(c);
}

        可以看出,ngx_http_process_request()方法主要完成了如下几部分的工作:

  • 检查了当前读事件是否超时了,如果超时了,则将其从事件框架中移除;
  • 如果读事件没超时,则会将读事件和写事件的回调函数都设置为ngx_http_request_handler()方法,这个方法在内部会根据事件的读写类型以判断是重新调用r->read_event_handler()还是r->write_event_handler()方法。r->read_event_handler()方法指针就是下面一行代码所设置的,即ngx_http_block_reading(),而r->write_event_handler()方法指针则是在下面的ngx_http_handler()方法中会进行设置,即ngx_http_core_run_phases()方法,这个方法是驱动http模块11个阶段的核心方法;
  • r->read_event_handler设置为ngx_http_block_reading,这个方法是一个空方法,其内部只是将当前的读事件移除,这主要是因为当前已经读取完了header数据,而body数据则是有http模块的11个阶段进行处理,因而当前不需要读取连接句柄上的数据;
  • 调用ngx_http_handler()方法以预处理请求,然后进入http模块11个阶段的处理;
  • 调用ngx_http_run_posted_requests()方法,这个方法的主要作用是依次调用当前请求的各个子请求。

2. http模块11个阶段

        进入http模块11个阶段的方法是ngx_http_handler(),这个方法首先会对当前的请求参数进行预处理,这里主要是判断当前请求是不是内部跳转,如果不是,则将请求的阶段从0开始,也即从最开始的一个阶段开始;如果是内部跳转,则直接让当前请求进入server_rewrite阶段。预处理完成后,就会调用ngx_http_core_run_phases()方法进入http模块11个阶段的链式流程:

/**
 * 当前方法首先设置当前请求的各个属性,然后调用http的11个阶段的checker()方法处理请求
 */
void ngx_http_handler(ngx_http_request_t *r) {
  ngx_http_core_main_conf_t *cmcf;
  r->connection->log->action = NULL;
  // r->internal为1时表示请求是在内部做跳转
  if (!r->internal) {
    switch (r->headers_in.connection_type) {
      case 0:
        r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
        break;
      case NGX_HTTP_CONNECTION_CLOSE:
        r->keepalive = 0;
        break;
      case NGX_HTTP_CONNECTION_KEEP_ALIVE:
        r->keepalive = 1;
        break;
    }

    r->lingering_close = (r->headers_in.content_length_n > 0 || r->headers_in.chunked);
    // 设置当前的处理阶段为初始阶段
    r->phase_handler = 0;
  } else {
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    // 指定当前的http阶段为server_rewrite_index
    r->phase_handler = cmcf->phase_engine.server_rewrite_index;
  }

  r->valid_location = 1;
#if (NGX_HTTP_GZIP)
  r->gzip_tested = 0;
  r->gzip_ok = 0;
  r->gzip_vary = 0;
#endif

  // 设置当前写事件的回调函数为ngx_http_core_run_phases()方法,这样就可以在发生写事件时再次驱动
  // http模块的11个阶段
  r->write_event_handler = ngx_http_core_run_phases;
  ngx_http_core_run_phases(r);
}

        这里主要需要强调的一点在于,r->write_event_handler()方法指针被指向为ngx_http_core_run_phases()方法,也就是说在发生写事件时,最终就会回调到这个方法。下面我们继续看ngx_http_core_run_phases()方法:

/**
 * 调用http请求的11个阶段的checker()方法以处理请求
 */
void ngx_http_core_run_phases(ngx_http_request_t *r) {
  ngx_int_t rc;
  ngx_http_phase_handler_t *ph;
  ngx_http_core_main_conf_t *cmcf;

  cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

  ph = cmcf->phase_engine.handlers;
  // 这里主要是调用http请求的11个阶段的各个checker()方法依次对请求进行处理
  // 这里的phase_handler是在ngx_http_handler()方法中进行设置的,指定了当前将要从http的哪个阶段
  // 开始执行,在调用每个checker()方法的时候,该方法内部会对r->phase_handler进行指定,
  // 以控制下一步将执行哪一阶段,因而这里没有看到任何控制r->phase_handler的调用
  while (ph[r->phase_handler].checker) {
    rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
    if (rc == NGX_OK) {
      return;
    }
  }
}

        ngx_http_core_run_phases()方法的整体流程是比较简单的,主要就是调用当前请求的各个阶段的回调方法,这里是以一种类似于责任链的方式进行请求的处理的。我们将在后面详细讲解http模块的11个阶段的整体结构,这里我们主要需要理解其控制这个链的方式。上面的链是以数组的方式进行组织的,链的切换是通过r->phase_handler索引进行的(这也是在前面的ngx_http_handler()方法中根据是否为内部调用而设置这个参数的原因),链的接口方法就是ph.checker(),这个方法会返回四种类型的返回值:

  • NGX_OK:表示当前阶段已经执行完毕,继续执行下一阶段,需要注意的是,这种情况下,当前阶段的后续方法都不会执行。这个参数能够控制继续执行下一阶段的原因在于,在返回NGX_OK之前,ph->checker()方法会将r->phase_handler指向ph->next,这里的ph->next指向的就是下一阶段的第一个节点的索引;
  • NGX_DECLINED:表示会继续执行当前阶段的后续方法。这里控制的主要方式是在返回NGX_DECLINED之前,会执行r->phase_handler++,这样就可以将索引切换到下一个节点;
  • NGX_AGAIN和NGX_DONE:表示当前checker方法无法在这一次调用中完成其工作,需要继续多次触发。可以看出来,这里多次触发的方式就是进行下一次的while循环。

3. 事件驱动方式

        上面的ph->checker()方法的返回值如果为NGX_OK,根据while循环的逻辑,就会终止当前方法的调用,此时就会将控制权交给事件驱动。前面我们讲到,当前请求的读写事件的回调函数都设置为了ngx_http_request_handler()方法,如下是该方法的源码:

/**
 * 当触发当前连接的读或写事件时,分别调用其write_event_handler()或者read_event_handler()方法
 */
static void ngx_http_request_handler(ngx_event_t *ev) {
  ngx_connection_t *c;
  ngx_http_request_t *r;

  c = ev->data;
  r = c->data;

  ngx_http_set_log_request(c->log, r);

  if (ev->delayed && ev->timedout) {
    ev->delayed = 0;
    ev->timedout = 0;
  }

  if (ev->write) {
    r->write_event_handler(r);
  } else {
    r->read_event_handler(r);
  }

  ngx_http_run_posted_requests(c);
}

        这里主要的逻辑就是根据当前的事件类型来控制调用r->write_event_handler()还是r->read_event_handler()。而前面我们讲到,r->read_event_handler指向的是ngx_http_block_reading()方法,这是一个空的回调方法;r->write_event_handler指向的是ngx_http_core_run_phases()方法,也就是我们上面讲解的执行http模块11个阶段的方法,也就是说,通过再次的事件调用,nginx可以控制当前请求继续进行之前未完成的阶段处理。

4. 小结

        本文主要讲解了nginx是如何开始进行http模块的11个阶段的驱动的,并且详细介绍了这11个阶段的链式处理方式,最后讲解了nginx是如何通过事件框架不断的驱动http模块11个阶段的运行的。

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
2 评论
0 收藏
0
分享
返回顶部
顶部