Der Inhalt dieses Artikels ist eine kurze Analyse des Nginx-HTTP-Verarbeitungsprozesses. Ich hoffe, dass er für Freunde hilfreich ist.
1. Initialisieren Sie den Server
Der Serverbefehl wird verwendet, um den virtuellen Server zu konfigurieren. Normalerweise konfigurieren wir mehrere virtuelle Server auf einer Maschine Portnummern, die verschiedenen Dateiverzeichnissen zugeordnet sind; Nginx analysiert die Benutzerkonfiguration, erstellt Sockets auf allen Ports und beginnt mit dem Abhören.
nginx-Parsing-Konfigurationsdateien werden von jedem Modul gemeinsam genutzt und verarbeitet. Jedes Modul registriert und verarbeitet die für es relevante Konfiguration, die über das Feld ngx_command_t *commands der Modulstruktur ngx_module_t implementiert wird Beispielsweise ist ngx_http_module ein Kernmodul, dessen Befehlsfeld wie folgt definiert ist:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
ngx_http_optimize_servers-Methode durchläuft alle Konfigurationsports, erstellt ein ngx_listening_t-Objekt und fügt es hinzu. Gehen Sie zu conf->cycle->listening (nachfolgende Vorgänge werden dieses Array durchlaufen, Sockets erstellen und abhören). Die Hauptoperation der Methode ist wie folgt:
Beachten Sie, dass der Handler von ngx_listening_t auf ngx_http_init_connection gesetzt ist, wenn eine Socket-Verbindungsanforderung empfangen wird.
Wann wird also mit der Überwachung begonnen? Die globale Suche nach Schlüsselwörtern „Cycle->Listening“ kann gefunden werden. Die Hauptmethode ruft ngx_init_cycle auf, wodurch der Großteil der Serverinitialisierungsarbeit abgeschlossen wird, einschließlich des Startens des Abhörens (ngx_open_listening_sockets).
Angenommen, Nginx verwendet Epoll, um alle Socket-Ereignisse zu verarbeiten. Wann werden die Abhörereignisse zu Epoll hinzugefügt? Die globale Suche nach Schlüsselwörtern „Cycle->Listening“ kann gefunden werden. Das Modul ngx_event_core_module ist das Kernmodul der Ereignisverarbeitung. Bei der Initialisierung dieses Moduls wird die Funktion ngx_event_process_init ausgeführt, in der die Listening-Ereignisse zu Epoll hinzugefügt werden.
1 2 3 4 5 6 7 8 9 10 |
|
Es ist mir aufgefallen, dass die Handlerfunktion zum Empfangen des Client-Socket-Verbindungsanforderungsereignisses ngx_event_accept ist.
2. HTTP-Anforderungsanalyse2.1 Grundstruktur
Die Struktur ngx_connection_t speichert Socket-Verbindungsinformationen; nginx erstellt im Voraus mehrere ngx_connection_t-Objekte In der globalen Variablen ngx_cycle->free_connections wird es als Verbindungspool bezeichnet. Wenn ein neuer Socket generiert wird, wird versucht, eine inaktive Verbindung aus dem Verbindungspool abzurufen. Wenn die Erfassung fehlschlägt, wird der Socket direkt geschlossen.
Die Anweisung worker_connections wird verwendet, um die maximale Anzahl von Verbindungen im Verbindungspool zu konfigurieren. Sie wird im Ereignisanweisungsblock konfiguriert und von ngx_event_core_module analysiert.
1 2 3 4 |
|
Wenn Nginx als HTTP-Server dient, beträgt die maximale Anzahl an Clients maxClient=worker_processes
worker_connections/2; wenn Nginx als Reverse-Proxy-Server dient, beträgt die maximale Anzahl an Clients maxClient=worker_processesworker_connections/4 . Seine worker_processes ist die Anzahl der vom Benutzer konfigurierten Worker-Prozesse. Die Struktur ngx_connection_t ist wie folgt definiert:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Die Struktur ngx_http_request_t speichert alle für den gesamten HTTP-Anforderungsverarbeitungsprozess erforderlichen Informationen. Es gibt nur eine kurze Beschreibung:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Das Parsen von Anforderungszeilen und Anforderungstexten ist relativ einfach. Hier konzentrieren wir uns auf das Parsen von Anforderungsheadern. Die analysierten Anforderungsheaderinformationen werden in der Struktur ngx_http_headers_in_t gespeichert.
Alle HTTP-Header sind in der Datei ngx_http_request.c definiert und im Array ngx_http_headers_in gespeichert. Jedes Element des Arrays ist eine ngx_http_header_t-Struktur, die hauptsächlich drei Felder enthält, den Header-Namen und das analysierte Header-Feld. Der in ngx_http_headers_in_t, der Verarbeitungsfunktion zum Parsen von Headern, gespeicherte Offset.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Suchen Sie beim Parsen des Anforderungsheaders das Anforderungsheader-Objekt ngx_http_header_t im Array ngx_http_headers_in, rufen Sie den Verarbeitungsfunktionshandler auf und speichern Sie es im entsprechenden Feld von r->headers_in. Am Beispiel des Parsens des Connection-Headers wird ngx_http_process_connection wie folgt implementiert:
1 2 3 4 5 6 7 8 9 10 11 |
|
Der Eingabeparameter-Offset hat hier keine Auswirkung. Beachten Sie, dass der zweite Eingabeparameter ngx_table_elt_t die Schlüssel-Wert-Paar-Informationen des aktuellen Anforderungsheaders speichert:
1 2 3 4 5 6 |
|
Denken Sie an ein anderes Problem, wenn Sie nach dem ngx_http_header_t-Objekt suchen, das dem Anforderungsheader aus dem ngx_http_headers_in-Array entspricht Um jedes Element zu durchlaufen, ist ein String-Vergleich erforderlich, der ineffizient ist. Daher konvertiert nginx das Array ngx_http_headers_in in eine Hash-Tabelle. Der Schlüssel der Hash-Tabelle ist der Schlüssel des Anforderungsheaders. Die Methode ngx_http_init_headers_in_hash implementiert die Konvertierung des Arrays in die Hash-Tabelle. >headers_in_hash-Feld.
第1节提到,在创建socket启动监听时,会添加可读事件到epoll,事件处理函数为ngx_event_accept,用于接收socket连接,分配connection连接,并调用ngx_listening_t对象的处理函数(ngx_http_init_connection)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
socket连接成功后,nginx会等待客户端发送HTTP请求,默认会有60秒的超时时间,即60秒内没有接收到客户端请求时,断开此连接,打印错误日志。函数ngx_http_init_connection用于设置读事件处理函数,以及超时定时器。
1 2 3 4 5 6 7 |
|
全局搜索post_accept_timeout字段,可以查找到设置此超时时间的配置指令,client_header_timeout,其可以在http、server指令块中配置。
函数ngx_http_wait_request_handler为解析HTTP请求的入口函数,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
函数ngx_http_create_request创建并初始化ngx_http_request_t对象,注意这赋值语句r->header_in =c->buffer。
解析请求行与请求头的代码较为繁琐,终点在于读取socket数据,解析字符串,这里不做详述。HTTP请求解析过程主要函数调用如下图所示:
注意,解析完成请求行与请求头,nginx就开始处理HTTP请求,并没有等到解析完请求体再处理。处理请求入口为ngx_http_process_request。
3.处理HTTP请求
3.1 HTTP请求处理的11个阶段
nginx将HTTP请求处理流程分为11个阶段,绝大多数HTTP模块都会将自己的handler添加到某个阶段(将handler添加到全局唯一的数组phases中),注意其中有4个阶段不能添加自定义handler,nginx处理HTTP请求时会挨个调用每个阶段的handler;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
nginx使用结构体ngx_module_s表示一个模块,其中字段ctx,是一个指向模块上下文结构体的指针(上下文结构体的字段都是一些函数指针);nginx的HTTP模块上下文结构体大多都有字段postconfiguration,负责注册本模块的handler到某个处理阶段。11个阶段在解析完成http配置块指令后初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
以限流模块ngx_http_limit_req_module模块为例,postconfiguration方法简单实现如下:
1 2 3 4 5 6 7 8 |
|
GDB调试,断点到ngx_http_block方法执行所有HTTP模块注册handler之后,打印phases数组
1 2 |
|
11个阶段注册的handler如下图所示:
3.2 11个阶段初始化
上面提到HTTP的11个处理阶段handler存储在phases数组,但由于多个模块可能注册handler到同一个阶段,使得phases是一个二维数组,因此需要转换为一维数组,转换后存储在cmcf->phase_engine字段,phase_engine的类型为ngx_http_phase_engine_t,定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
数组转换函数ngx_http_init_phase_handlers实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
|
GDB打印出转换后的数组如下图所示,第一列是cheker字段,第二列是handler字段,箭头表示next跳转;图中有个返回的箭头,即NGX_HTTP_POST_REWRITE_PHASE阶段可能返回到NGX_HTTP_FIND_CONFIG_PHASE;原因在于只要NGX_HTTP_REWRITE_PHASE阶段产生了url重写,就需要重新查找匹配location。
3.3 处理HTTP请求
2.2节提到HTTP请求的处理入口函数是ngx_http_process_request,其主要调用ngx_http_core_run_phases实现11个阶段的执行流程;
ngx_http_core_run_phases遍历预先设置好的cmcf->phase_engine.handlers数组,调用其checker函数,逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
checker内部就是调用handler,并设置下一步要执行handler的索引;比如说ngx_http_core_generic_phase实现如下:
1 2 3 4 5 6 7 8 9 |
|
3.4 内容产生阶段
内容产生阶段NGX_HTTP_CONTENT_PHASE是HTTP请求处理的第10个阶段,一般情况有3个模块注册handler到此阶段:ngx_http_static_module、ngx_http_autoindex_module和ngx_http_index_module。
但是当我们配置了proxy_pass和fastcgi_pass时,情况会有所不同;
使用proxy_pass配置上游时,ngx_http_proxy_module模块会设置其处理函数到配置类conf;使用fastcgi_pass配置时,ngx_http_fastcgi_module会设置其处理函数到配置类conf。例如:
1 2 3 4 5 6 7 |
|
阶段NGX_HTTP_FIND_CONFIG_PHASE查找匹配的location,并获取此ngx_http_core_loc_conf_t对象,将其handler赋值给ngx_http_request_t对象的content_handler字段(内容产生处理函数)。
而在执行内容产生阶段的checker函数时,会执行content_handler指向的函数;查看ngx_http_core_content_phase函数实现(内容产生阶段的checker函数):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
总结
nginx处理HTTP请求的流程较为复杂,因此本文只是简单提供了一条线索:分析了nginx服务器启动监听的过程,HTTP请求的解析过程,11个阶段的初始化与调用过程。至于HTTP解析处理的详细流程,还需要读者去探索。
Das obige ist der detaillierte Inhalt vonEine kurze Analyse des Nginx-HTTP-Verarbeitungsablaufs. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!