Maison > Opération et maintenance > Nginx > le corps du texte

Comment nginx gère les requêtes http

PHPz
Libérer: 2023-06-03 18:41:31
avant
1947 Les gens l'ont consulté

1. Interaction entre l'événement et le framework http

Après avoir reçu la ligne de requête http et l'en-tête de requête http, la fonction ngx_http_process_request sera appelée pour commencer le traitement de la requête http. Étant donné qu'une requête http se compose de 11 étapes de traitement et que chaque étape de traitement permet à plusieurs modules http d'intervenir, donc dans cette fonction, les modules http à chaque étape seront programmés pour terminer la requête ensemble. La fonction

//接收到http请求行与请求头后,http的处理流程,是第一个http处理请求的读事件回调 
//这个函数执行后,将把读写事件的回调设置为ngx_http_request_handler。这样下次再有事件时 
//将调用ngx_http_request_handler函数来处理,而不会再调用ngx_http_process_request了 
static void ngx_http_process_request(ngx_http_request_t *r) 
{ 
  ngx_connection_t *c; 
  c = r->connection; 
  //因为已经接收完http请求行、请求头部了,准备调用各个http模块处理请求了。 
  //因此需要接收任何来自客户端的读事件,也就不存在接收http请求头部超时问题 
  if (c->read->timer_set)  
  { 
    ngx_del_timer(c->read); 
  } 
  //重新设置当前连接的读写事件回调 
  c->read->handler = ngx_http_request_handler; 
  c->write->handler = ngx_http_request_handler; 
  //设置http请求对象的读事件回调,这个回调不做任何的事情。 
  //那http请求对象的读事件回调,与上面的连接对应的读事件回调有什么关系呢? 
  //当读事件发生后,连接对应的读事件回调ngx_http_request_handler会被调用, 
  //在这个回调内会调用http请求对象的读事件回调ngx_http_block_reading,而这个回调是 
  //不会做任何事件的,因此相当于忽略了读事件。因为已经接收完了请求行请求头,现在要做的是调用各个http模块, 
  //对接收到的请求行请求头进行处理 
  r->read_event_handler = ngx_http_block_reading; 
 
  //调用各个http模块协同处理这个请求 
  ngx_http_handler(r); 
  //处理子请求 
  ngx_http_run_posted_requests(c); 
}
Copier après la connexion

ngx_http_process_request ne sera appelée qu'une seule fois. Si une planification ne peut pas gérer les 11 étapes http, les rappels d'événements de lecture et d'écriture correspondant à l'objet de connexion seront définis sur ngx_http_request_handler. L'événement de lecture de l'objet de requête est défini sur ngx_http_block_reading et le rappel d'événement d'écriture de l'objet de requête est défini sur ngx_http_core_run_phases. Ce rappel est défini dans ngx_http_handler. De cette façon, la fonction

ngx_http_process_request ne sera pas appelée lorsque l'événement se reproduira. Alors, quelle est la relation entre les rappels d'événements de lecture et d'écriture du module d'événement d'événement et les rappels d'événements de lecture et d'écriture de l'objet de requête http

Comment nginx gère les requêtes http

//http请求处理读与写事件的回调,在ngx_http_process_request函数中设置。 
//这个函数中将会调用http请求对象的读写事件回调。将event事件模块与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 
  } 
 
  //处理子请求 
  ngx_http_run_posted_requests(c); 
}
Copier après la connexion

Vous pouvez voir cela dans le rappel d'événement de lecture de l'objet de connexion ? le rappel de l'événement de lecture de l'objet de requête http sera appelé. Le rappel d'événement d'écriture de l'objet de connexion appellera le rappel d'événement d'écriture de l'objet de requête http.

Comment nginx gère les requêtes http

On peut voir sur la figure que lorsque l'événement read de l'événement se produit, le rappel ngx_http_request_handler de l'événement read sera appelé après le retour d'epoll. Dans ce rappel d'événement de lecture, le framework http sera appelé, c'est-à-dire le rappel d'événement de lecture ngx_http_block_reading de l'objet de requête http. Le rappel d'événement de lecture de cet objet de requête http ne fait rien, ce qui équivaut à ignorer l'événement de lecture. Par conséquent, la trame http reviendra au module événementiel. Alors pourquoi ignorer l'événement read ? Parce que toutes les lignes de requête http et les en-têtes de requête ont été reçus. Ce que nous devons faire maintenant est de planifier les différents modules http pour qu'ils travaillent ensemble pour terminer le traitement des lignes de requête et des en-têtes de requête reçus. Il n’est donc pas nécessaire de recevoir des données du client.的 件 La gestion de l'événement est beaucoup plus compliquée. Lorsque l'événement d'écriture d'événement se produit, après le retour d'EPOLL, le rappel de l'événement d'écriture sera appelé NGX_HTTP_REQUEST_HANDLER Le rappel d'événement d'écriture de l'objet de requête ngx_http_core_run_phases. Le rappel de ce framework http planifiera les méthodes de gestion de chaque module http impliqué dans les 11 étapes de la requête pour compléter conjointement la requête http.

Comment nginx gère les requêtes http2. Planification du module http pour traiter la requête

Dans le code ci-dessus, la fonction ngx_http_core_run_phases sera programmée pour que chaque module http puisse intervenir dans la requête http. Et cette fonction est définie dans ngx_http_handler.

//调用各个http模块协同处理这个请求 
void ngx_http_handler(ngx_http_request_t *r) 
{ 
  //不需要进行内部跳转。什么是内部跳转? 例如有个location结构,里面的 
  // 
  if (!r->internal)   
  { 
    //将数组序号设为0,表示从数组第一个元素开始处理http请求 
    //这个下标很重要,决定了当前要处理的是11个阶段中的哪一个阶段, 
    //以及由这个阶段的哪个http模块处理请求 
    r->phase_handler = 0; 
  }  
  else  
  { 
    //需要做内部跳转 
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); 
    //将序号设置为server_rewrite_index 
    r->phase_handler = cmcf->phase_engine.server_rewrite_index; 
  } 
  //设置请求对象的写事件回调,这个回调将会调度介入11个http阶段的各个http模块 
  //共同完成对请求的处理 
  r->write_event_handler = ngx_http_core_run_phases; 
  //开始调度介入11个http阶段的各个http模块 
  ngx_http_core_run_phases(r); 
}
Copier après la connexion
La fonction ngx_http_core_run_phases est très simple. Elle planifie les méthodes de vérification de tous les modules http impliqués dans 11 étapes de traitement http.
//调用各个http模块协同处理这个请求, checker函数内部会修改phase_handler 
void ngx_http_core_run_phases(ngx_http_request_t *r) 
{ 
  cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); 
  ph = cmcf->phase_engine.handlers; 
  //调用各个http模块的checker方法,使得各个http模块可以介入http请求 
  while (ph[r->phase_handler].checker) 
  { 
    rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); 
    //从http模块返回ngx_ok,http框架则会把控制全交还给事件模块 
    if (rc == ngx_ok)     
    { 
      return; 
    } 
  }
Copier après la connexion

Supposons qu'il y ait trois modules http impliqués dans la requête http à l'étape 2, un module impliqué dans la requête http à l'étape 3 et un module impliqué dans la requête à l'étape 4. Lorsque la phase 2 commence à être traitée, tous les modules http de la phase 2 seront appelés pour le traitement. À ce moment, phase_handler pointe vers la position de départ de la phase 2. Une fois chaque module de la phase 2 traité, phase_handler pointe vers le module suivant de la phase 2 jusqu'à ce que le traitement de la phase 2 soit terminé.的 Lorsque tous les modules HTTP de l'étape 2 seront traités, Phase_handler pointera vers la période 3. Comme l'étape 3 n'a qu'un seul module HTTP, tous les modules HTTP de l'étape 3 sont traités pour se terminer. Quand, phase_handler pointera vers la phase 4

Alors, quand ce tableau de gestionnaires a-t-il été créé ? Que fait le rappel du vérificateur de chaque module http ? Ensuite, nous analyserons ces deux questions Comment nginx gère les requêtes http

Trois, 11 création d'un tableau d'étape de requête http

Lors de l'analyse de la configuration de nginx.conf fichier, lorsque le bloc http est analysé, la fonction ngx_http_block sera appelée pour commencer l'analyse du bloc http. Dans cette fonction, tous les modules http devant intervenir dans les 11 étapes de la requête http seront également enregistrés dans le tableau. Comment nginx gère les requêtes http

//开始解析http块 
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 
{ 
  //http配置解析完成后的后续处理,使得各个http模块可以介入到11个http阶段 
  for (m = 0; ngx_modules[m]; m++)  
  { 
    if (ngx_modules[m]->type != ngx_http_module)  
    { 
      continue; 
    } 
 
    module = ngx_modules[m]->ctx; 
    if (module->postconfiguration)  
    { 
      //每一个http模块的在这个postconfiguration函数中,都可以把自己注册到11个http阶段 
      if (module->postconfiguration(cf) != ngx_ok)  
      { 
        return ngx_conf_error; 
      } 
    } 
  } 
}
Copier après la connexion

Par exemple, le module statique ngx_http_static_module définira le rappel de la phase ngx_http_content_phase qui intervient dans les 11 phases http sur ngx_http_static_handler

//静态模块将自己注册到11个http请求阶段中的ngx_http_content_phase阶段 
static ngx_int_t ngx_http_static_init(ngx_conf_t *cf) 
{ 
  cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 
  h = ngx_array_push(&cmcf->phases[ngx_http_content_phase].handlers); 
 
  //静态模块在ngx_http_content_phase阶段的处理方法 
  *h = ngx_http_static_handler; 
 
  return ngx_ok; 
}
Copier après la connexion

例如: ngx_http_access_module访问权限模块,会将自己介入11个http阶段的ngx_http_access_phase阶段回调设置为ngx_http_access_handler

//访问权限模块将自己注册到11个http请求阶段中的ngx_http_access_phase阶段 
static ngx_int_t ngx_http_access_init(ngx_conf_t *cf) 
{ 
  cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 
  h = ngx_array_push(&cmcf->phases[ngx_http_access_phase].handlers); 
   
  //访问权限模块在ngx_http_access_phase阶段的处理方法 
  *h = ngx_http_access_handler; 
   
  return ngx_ok; 
}
Copier après la connexion

上面的这些操作,只是把需要介入到11个http阶段的http模块保存到了ngx_http_core_main_conf_t中的phases成员中,并没有保存到phase_engine中。那什么时候将phases的内容保存到phase_engine中呢? 还是在ngx_http_block函数中完成

//开始解析http块 
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 
{ 
    //初始化请求的各个阶段 
  if (ngx_http_init_phase_handlers(cf, cmcf) != ngx_ok)  
  { 
    return ngx_conf_error; 
  } 
}
Copier après la connexion

假设阶段1有一个http模块介入请求,阶段2有三个http模块介入请求、阶段3也有一个http模块介入请求。则ngx_http_init_phase_handlers这个函数调用后,从ngx_http_phase_t phases[11]数组转换到ngx_http_phase_handler_t handlers数组的过程如下图所示:

Comment nginx gère les requêtes http

//初始化请求的各个阶段 
static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) 
{ 
  //11个http请求阶段,每一个阶段都可以有多个http模块介入。 
  //这里统计11个节点一共有多个少http模块。以便下面开辟空间 
  for (i = 0; i < ngx_http_log_phase; i++)  
  { 
    n += cmcf->phases[i].handlers.nelts; 
  } 
  //开辟空间,存放介入11个处理阶段的所有http模块的回调 
  ph = ngx_pcalloc(cf->pool,n * sizeof(ngx_http_phase_handler_t) + sizeof(void *)); 
  cmcf->phase_engine.handlers = ph; 
  n = 0; 
 
  //对于每一个http处理阶段,给该阶段中所有介入的http模块赋值 
  for (i = 0; i < ngx_http_log_phase; i++) 
  { 
    h = cmcf->phases[i].handlers.elts; 
 
    switch (i)  
    { 
      case ngx_http_server_rewrite_phase://根据请求的uri查找location之前,修改请求的uri阶段 
        if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1)  
        { 
          cmcf->phase_engine.server_rewrite_index = n; //重定向模块在数组中的位置 
        } 
        checker = ngx_http_core_rewrite_phase;   //每一个阶段的checker回调 
        break; 
      case ngx_http_find_config_phase://根据请求的uri查找location阶段(只能由http框架实现) 
        find_config_index = n; 
        ph->checker = ngx_http_core_find_config_phase; 
        n++; 
        ph++; 
        continue; 
      case ngx_http_rewrite_phase:  //根据请求的rui查找location之后,修改请求的uri阶段 
        if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) 
        { 
          cmcf->phase_engine.location_rewrite_index = n; 
        } 
        checker = ngx_http_core_rewrite_phase; 
        break; 
      case ngx_http_post_rewrite_phase: //ngx_http_rewrite_phase阶段修改rul后,防止递归修改uri导致死循环阶段 
        if (use_rewrite)  
        { 
          ph->checker = ngx_http_core_post_rewrite_phase; 
          ph->next = find_config_index;//目的是为了地址重写后,跳转到ngx_http_find_config_phase阶段,根据 
                        //url重写查找location 
          n++; 
          ph++; 
        } 
        continue; 
      case ngx_http_access_phase:     //是否允许访问服务器阶段 
        checker = ngx_http_core_access_phase; 
        n++; 
        break; 
      case ngx_http_post_access_phase:  //根据ngx_http_access_phase阶段的错误码,给客户端构造响应阶段 
        if (use_access)  
        { 
          ph->checker = ngx_http_core_post_access_phase; 
          ph->next = n; 
          ph++; 
        } 
        continue; 
      case ngx_http_try_files_phase:   //try_file阶段 
        if (cmcf->try_files)  
        { 
          ph->checker = ngx_http_core_try_files_phase; 
          n++; 
          ph++; 
        } 
        continue; 
      case ngx_http_content_phase:    //处理http请求内容阶段,大部分http模块最愿意介入的阶段 
        checker = ngx_http_core_content_phase; 
        break; 
      default: 
        //ngx_http_post_read_phase,  
        //ngx_http_preaccess_phase,  
        //ngx_http_log_phase三个阶段的checker方法 
        checker = ngx_http_core_generic_phase; 
    } 
    n += cmcf->phases[i].handlers.nelts; 
    //每一个阶段中所介入的所有http模块,同一个阶段中的所有http模块有唯一的checker回调, 
    //但handler回调每一个模块自己实现 
    for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--)  
    { 
      ph->checker = checker;     
      ph->handler = h[j]; 
      ph->next = n; 
      ph++; 
    } 
  } 
  return ngx_ok; 
}
Copier après la connexion

四、http阶段的checker回调

在11个http处理阶段中,每一个阶段都有一个checker函数,当然有些阶段的checker函数是相同的。对每一个处理阶段,介入这个阶段的所有http模块都共用同一个checker函数。这些checker函数的作用是调度介入这个阶段的所有http模块的handler方法,或者切换到一下个http请求阶段。下面分析下ngx_http_post_read_phase,ngx_http_preaccess_phase,ngx_http_log_phase三个阶段的checker方法。

//ngx_http_post_read_phase,  
//ngx_http_preaccess_phase,  
//ngx_http_log_phase三个阶段的checker方法 
//返回值: ngx_ok,http框架会将控制权交还给epoll模块 
ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,ngx_http_phase_handler_t *ph) 
{ 
  ngx_int_t rc; 
  //调用http模块的处理方法,这样这个http模块就介入到了这个请求阶段 
  rc = ph->handler(r); 
  //跳转到下一个http阶段执行 
  if (rc == ngx_ok)         
  { 
    r->phase_handler = ph->next; 
    return ngx_again; 
  } 
   
  //执行本阶段的下一个http模块 
  if (rc == ngx_declined)      
  { 
    r->phase_handler++; 
    return ngx_again; 
  } 
 
  //表示刚执行的handler无法在这一次调度中处理完这一个阶段, 
  //需要多次调度才能完成 
  if (rc == ngx_again || rc == ngx_done)  
                       
  { 
    return ngx_ok; 
  } 
  //返回出错 
  /* rc == ngx_error || rc == ngx_http_... */ 
  ngx_http_finalize_request(r, rc); 
 
  return ngx_ok; 
}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal