点赞
评论
收藏
分享
举报
nginx配置解析原理(二)
发表于2020-09-23 16:15

浏览 748

前面的nginx配置原理解析(一)我们重点阐述了nginx配置的起源,同时以http模块的配置为例勾勒了nginx配置数据结构在内存中的映像。本文继续(一)未完成的部分,仔细描述http块内的配置解析,同时为读者勾画整个解析完成后内存配置结构。

预备知识

nginx http模块的配置一般形式如下:

  1. http {
  2. test_str "hi"
  3. ...
  4. server {
  5. ...
  6. test_str "how"
  7. location /hello {
  8. ...
  9. }
  10. location /hi {
  11. ...
  12. test_str "how are u"
  13. }
  14. ...
  15. }
  16. server {
  17. location /x {
  18. ...
  19. }
  20. location /y {
  21. ...
  22. }
  23. }
  24. }

http 块下面可以包含server块,而server块内也可嵌套location块,同时,每个模块的可以根据自己的需求将同一个配置同时出现在http、server、location块中,但是后续也必须得由自己决定,如上面示例的test_str。

nginx配置解析过程

在这里,我们简单描述下nginx配置解析过程以及函数调用关系。首先,nginx由许多模块构成,每个配置项应该是由其中的某个模块来解析,因此,整个模块解析过程其实就是“加载-发现-解析”的循环。

所有的模块配置解析都是由ngx_conf_parse()发起的。在这里先调用ngx_read_token()读取一个配置项,然后调用ngx_conf_handler()来解析配置项。以http模块为例,如遇到"http"这个指令时,会调用ngx_http_block()来解析整个http模块的配置。

关于ngx_http_block()函数初始化我们已经在(一)中仔细描述过了,并且给出了在内存中的映像图,今天我们就接着这个继续往下。

ngx_http_block

在(一)中我们讲到了ngx_http_block()过程的初始化,但是具体的解析我们未涉及。

  1. for (m = 0; ngx_modules[m]; m++) {
  2. if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
  3. continue;
  4. }
  5. module = ngx_modules[m]->ctx;
  6. mi = ngx_modules[m]->ctx_index;
  7. if (module->create_main_conf) {
  8. ctx->main_conf[mi] = module->create_main_conf(cf);
  9. if (ctx->main_conf[mi] == NULL) {
  10. return NGX_CONF_ERROR;
  11. }
  12. }
  13. if (module->create_srv_conf) {
  14. ctx->srv_conf[mi] = module->create_srv_conf(cf);
  15. if (ctx->srv_conf[mi] == NULL) {
  16. return NGX_CONF_ERROR;
  17. }
  18. }
  19. if (module->create_loc_conf) {
  20. ctx->loc_conf[mi] = module->create_loc_conf(cf);
  21. if (ctx->loc_conf[mi] == NULL) {
  22. return NGX_CONF_ERROR;
  23. }
  24. }
  25. }

这里我们调用每个模块的三个回调函数(不一定需要全部实现),并在内存中创建其映像关系。之所以需要创建loc_conf、server_conf、main_conf是因为nginx的某个配置可能会同时出现在main、server、location范围,我们需要创建这些映像以便于合并。

当准备工作完成以后,会正式进入配置解析流程

  1. /* parse inside the http{} block */
  2. cf->module_type = NGX_HTTP_MODULE;
  3. cf->cmd_type = NGX_HTTP_MAIN_CONF;
  4. rv = ngx_conf_parse(cf, NULL);
  5. if (rv != NGX_CONF_OK) {
  6. goto failed;
  7. }

还是调用ngx_conf_parse()来解析配置,只是此时配置环境的上下文已经发生改变,cf->module_type = NGX_HTTP_MODULE,cf->cmd_type = NGX_HTTP_MAIN_CONF。表示此时要解析的是http模块的main block。

ngx_http_core_server

我们接下来重点研究对"server"指令的解析。整个调用的路径又变成了ngx_conf_parse()->ngx_conf_handler()。而ngx_conf_handler搜索到"server"指令的处理函数其实是由ngx_http_core_module的ngx_http_core_server()方法来解析。我们只关注这个函数最核心的部分:

  1. static char *
  2. ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
  3. {
  4. ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
  5. if (ctx == NULL) {
  6. return NGX_CONF_ERROR;
  7. }
  8. http_ctx = cf->ctx;
  9. ctx->main_conf = http_ctx->main_conf;
  10. /* the server{}'s srv_conf */
  11. ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
  12. if (ctx->srv_conf == NULL) {
  13. return NGX_CONF_ERROR;
  14. }
  15. /* the server{}'s loc_conf */
  16. ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
  17. if (ctx->loc_conf == NULL) {
  18. return NGX_CONF_ERROR;
  19. }
  20. for (i = 0; ngx_modules[i]; i++) {
  21. if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
  22. continue;
  23. }
  24. module = ngx_modules[i]->ctx;
  25. if (module->create_srv_conf) {
  26. mconf = module->create_srv_conf(cf);
  27. if (mconf == NULL) {
  28. return NGX_CONF_ERROR;
  29. }
  30. ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;
  31. }
  32. if (module->create_loc_conf) {
  33. mconf = module->create_loc_conf(cf);
  34. if (mconf == NULL) {
  35. return NGX_CONF_ERROR;
  36. }
  37. ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
  38. }
  39. }
  40. /* the server configuration context */
  41. cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
  42. cscf->ctx = ctx;
  43. // cmcf是http_ctx->main_conf[ngx_http_core_module]
  44. cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
  45. // 这个函数获取到数组第一个可用的项
  46. cscfp = ngx_array_push(&cmcf->servers);
  47. if (cscfp == NULL) {
  48. return NGX_CONF_ERROR;
  49. }
  50. *cscfp = cscf;
  51. /* parse inside server{} */
  52. pcf = *cf;
  53. cf->ctx = ctx;
  54. cf->cmd_type = NGX_HTTP_SRV_CONF;
  55. rv = ngx_conf_parse(cf, NULL);
  56. *cf = pcf;
  57. return rv;
  58. }

这个函数

  1. 首先为自己分配了一个ngx_http_conf_ctx_t结构;
  2. 初始化该结构,将该结构的main_conf指向http_ctx的main_conf,同时创建srv_conf和loc_conf指针数组,
  3. 依此调用每个http模块的create_srv_conf和create_loc_conf的回调方法(可不必实现),

接下来,就解析位于server{}块内的配置了,解析的方法也是调用ngx_conf_parse,只是此时的上下文环境又发生了改变:cf->ctx = ctx;cf->cmd_type = NGX_HTTP_SRV_CONF。同时在此之前,会将该server对应的ngx_http_conf_ctx_t结构放在cmcf->servers数组中:

  1. // cmcf是http_ctx->main_conf[ngx_http_core_module]
  2. cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
  3. // 这个函数获取到数组第一个空闲项
  4. cscfp = ngx_array_push(&cmcf->servers);
  5. if (cscfp == NULL) {
  6. return NGX_CONF_ERROR;
  7. }
  8. *cscfp = cscf;

用文字来表述也就是:在http{}对应的ngx_http_core_module的main配置ngx_http_core_main_conf_t中有一个数组servers存储了http{}块下的所有的server的结构,主要是便于后续merge。以后在解析的时候没遇到一个server {},就会将该server对应的数据结构添加到servers数组中。

至此,nginx解析到这里的时候在内存中形成的配置项结构如下图所示:


对上图的理解:

  1. 对于http模块的每个层级配置,均有一个ngx_http_conf_ctx_t结构与之对应,如"http"指令对应的粉色的方框以及两个server指令对应的蓝色方框;
  2. http块下所有的server块对应的ngx_http_conf_ctx_t结构均在存储在http块对应的ngx_http_conf_ctx_t的main_conf的ngx_http_core模块的ngx_http_core_main_conf_t的servers字段中,该字段的类型是array;
  3. 每个server的ngx_http_conf_ctx_t内又会创建srv_conf和loc_conf数组,以解析server{}内的配置,但不需要创建main_conf数组了,main_conf直接指向父配置块的main_conf,如图上的两条虚线
已修改于2023-03-09 02:25
创作不易,留下一份鼓励
皮皮鲁

暂无个人介绍

关注



写下您的评论
发表评论
全部评论(0)

按点赞数排序

按时间排序

关于作者
皮皮鲁
这家伙很懒还未留下介绍~
85
文章
2
问答
41
粉丝
相关文章
上一篇文章中,我介绍了Nginx的特性,如何获取Nginx源代码,以及源代码中各目录的含义。本文将介绍如何定制化编译、安装、运行Nginx。当你用yum或者apt-get命令安装、启动Nginx后,通过nginx-t命令你会发现,nginx.conf配置文件可能在/etc/目录中。而运行基于源码安装的Nginx时,nginx.conf文件又可能位于/usr/local/nginx/conf/目录,运行OpenResty时, nginx.conf又被放在了/usr/local/openresty/nginx/conf/目录。这些奇怪的现象都源于编译Nginx前,configure脚本设置的--prefix或者--conf-path选项。Nginx的所有功能都来自于官方及第三方模块,如果你不知道如何使用configure添加需要的模块,相当于放弃了Nginx诞生16年来累积出的丰富生态。而且,很多高性能特性默认是关闭的,如果你习惯于使用应用市场中编译好的二进制文件,也无法获得性能最优化的Nginx。本文将会介绍定制Nginx过程中,configure脚本的用法。其中对于定制模块的选项,
点赞 8
浏览 5k
Namezstd-nginx-module-Nginxmoduleforthe Zstandardcompression.TableofContentNameStatusSynopsisInstallationDirectivesngx_http_zstd_filter_modulezstd_dict_filezstdzstd_comp_levelzstd_min_lengthzstd_typeszstd_buffersngx_http_zstd_static_modulezstd_staticVariablesngx_http_zstd_filter_module$zstd_ratioAuthorStatusThisNginxmoduleiscurrentlyconsideredexperimental.IssuesandPRsarewelcomeifyouencounteranyproblems.Synopsis#specifythedictionary zstd_dict_file/path/to/dict;
点赞 0
浏览 1.4k
尊敬的各位Nginx用户: 风险描述:服务器发出的UDP)可能引起一个字节的内存重写,从而可能导致NGINXworker远程攻击者可能导致NGINXworker进程停止响应,从而拒绝某些用户的访问。不会配置任何解析器。如果客户上线配置中不包含resolver受影响版本以及升级建议: 用户,受影响版本为:CVE
点赞 0
浏览 9.2k