运营研发团队 李乐

配置文件是nginx的基础,对于学习nginx源码甚至开发nginx模块的同学来说更是必须深究。本文将从源码从此深入分析nginx配置文件的解析,配置存储,与配置查找。

看本文之前读者可以先思考两个问题:

  • 1.nginx源码中随处可以看到类似于这样的代码。
//获取限流相关配置
lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
//获取fastcgi相关配置
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

为什么可以这样获取到限流和fastcgi相关的配置呢?

  • 2.server配置块中可以有多个location配置,那么location配置的匹配优先级是怎样的?比如说配置了三个location:“location ^~ /a { }”、“location /a/b { }”和“location ~ /a/* { }”,请求url为/a/b,最终能匹配到哪个location配置块呢?

相信学习本文之后,这两个问题将不在话下。

在学习nginx配置文件的解析过程之前,需要先了解一下nginx模块与指令的一些基本知识。

nginx的配置指令可以分为两大类:指令块(如events、http、server和location)与单条指令(如worker_processes、root、rewrite等)。

nginx规定指令块可以嵌套(如http块中可以嵌套server指令,server块中可以嵌套location指令),指令可以同时出现在不同的指令块(如root指令可以同时出现在http、server和location指令块)。

配置文件这种层次的复杂性,导致配置文件的解析与存储等的复杂性。

1.1 nginx模块

结构体ngx_module_t用于定义一个nginx模块,这里需要重点关注以下几个字段。

struct ngx_module_s {ngx_uint_t            ctx_index; //用于给同类型的模块编号ngx_uint_t            index;  //用于给所有模块编号void                 *ctx;  //模块上下文;很重要;不同类型的模块通常指向不同类型的结构体,结构体通常包含若干函数指针ngx_command_t        *commands; //指令数组ngx_uint_t            type;  //模块类型编码

type字段表示模块类型编码。ctx指向模块上下文结构体,且不同类型的模块通常指向不同类型的结构体,该结构体中通常会包含若干函数指针。

nginx常用模块可以分为这么几类:核心模块,事件模块语http模块(conf类模块与mail类模块暂不考虑)。见下表

从上表列出的三种类型的模块上下文结构体可以看出:

  • 1)核心模块上下文结构只有三个字段:name表示核心模块名称;create_conf用于创建模块配置结构体;init_conf用于初始化模块配置结构体;
  • 2)事件模块上下文结构前三个字段与核心模块相同,但是多了一个类型为ngx_event_actions_t结构的字段;该结构同样包含若干函数指针,表示该事件模块对外提供的若干API,比如添加事件还与删除事件等,这里不做详述;
  • 3)我们都知道http相关配置可以分为三类,http指令块、server指令块和location指令块,对应的配置结构体称为main_conf、srv_conf和loc_conf;相应的create_conf和init_conf方法用于创建和初始化相关配置结构体。

而http模块上下文结构的preconfiguration和postconfiguration用于初始化http处理流程相关操作。

index字段用于给所有模块编号,比如:

ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {ngx_modules[i]->index = ngx_max_module++;
}
ctx_index用于给同类型的模块编号,比如:ngx_http_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {if (ngx_modules[m]->type != NGX_HTTP_MODULE) {continue;}ngx_modules[m]->ctx_index = ngx_http_max_module++;
}

1.2 nginx配置指令

nginx的各个模块组合形成了其强大的处理能力,而每个模块只实现一个特定的功能。比如限流功能由模块ngx_http_limit_conn_module或者模块实现ngx_http_limit_req_module;fastcgi转发功能由模块ngx_http_fastcgi_module

实现;proxy转发功能由ngx_http_proxy_module(当然转发功能的实现还必须有模块ngx_http_upstream_module)。

当我们配置了指令proxy_pass或者fastcgi_pass时,该指令应该由哪个模块来解析呢?显然应该由实现此功能的模块来解析。即nginx配置文件的解析是分散到各个模块的。

每个模块都有一个commands数组,存储该模块可以解析的所有配置指令。指令结构体由ngx_command_t定义:

struct ngx_command_s {ngx_str_t             name;ngx_uint_t            type;char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);ngx_uint_t            conf;ngx_uint_t            offset;void                 *post;
};
  • name:配置指令名称,如“proxy_pass”;
  • type:指令类型,可以将指令类型分为两类,1)说明指令可以出现的位置,比如配置文件(只能在配置文件最外层,不能出现在任何指令块内部),http指令块,或者events指令块,或者server指令块,或者location指令块;

用于校验参数数目。常用指令类型如下:

  • set:处理函数,当读取到该配置指令时,会执行此函数;
  • conf和offset其实都表示的是偏移量,但是用处不同,解析指令时会详述,这里暂时跳过。
  • post可以指向多种结构,不同指令可能不同,大多都为NUll,解析到具体指令时会详述,这里同样跳过。

下面这张图展示了指令的基本分类(通过颜色区分,各种颜色的文字描述指令类型以及该指令只能被哪种类型的模块解析):

1.3 配置存储格式方案设计

http配置相对复杂,h指令块嵌套,模块众多,导致http配置解析与存储的复杂性。因此本小节重点讲述http相关配置存储的方案设计。

前面提到每个模块负责解析和存储自己关心的配置指令,即每个模块都应该有个可以存储配置的结构体,该结构体通过模块上下文结构体的函数create_conf,create_main_conf,create_srv_conf或者create_loc_conf创建。

比如说如下表:

问题来了,每个模块创建自己的配置结构体,存储是完全分散的,如何能快速查找到这些配置结构体呢?

最容易想到的就是声明一个void*的数组,数组元素数目就是模块数目,以模块的index字段作为数组的索引,数组的每个元素都指向对应模块的配置结构体。

但是不要忘记,nginx配置文件是有层次结构的,如http指令块中可以声明多个单条指令和多个server指令块,server指令块中可以声明多个单条指令和多个location指令块,location配置又可以声明多个单条指令。

我们可以这样来设计:

  • 1)配置文件可以包含多条指令,指令块同样可以包含多条指令,为此我们可以定义指令作用域或者称为指令上下文;

  • 2)指令块的嵌套等价为上下文的嵌套,而上下文表现为某种类型的结构体,因此可通过结构体的互相引用实现指令块的嵌套;
  • 3)指令或者指令块只能被特定类型的模块解析。比如,配置文件上下文包含的所有指令只能被核心模块(NGX_CORE_MODULE)解析;events指令块包含的所有指令只能被事件模块(NGX_EVENT_MODULE)解析;

http指令块内包含的所有指令或者指令块只能被http模块(NGX_HTTP_MODULE)解析。

  • 4)http模块可以解析http指令块,server指令块和location指令块的指令;因此,http模块的指令结构分为3种:main_conf、srv_conf和loc_conf,其通过函数create_main_conf,create_srv_conf和create_loc_conf创建。

参考这四点设计,我们可以简单画出http配置存储结构示意图:

这个结构似乎是可以的,但是我们忘记了一件事:一些指令可以同时出现在http指令块、server指令块和location指令块。

即http块中的指令类型可以是NGX_HTTP_MAIN_CONF,也可以是NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF,还可以是NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF;

而server块中的指令类型可以是NGX_HTTP_SRV_CONF,也可以是NGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF。(位或运算表示同时属于多种类型)

比如说指令root的类型位NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF,此时该配置应该存储在loc_conf配置结构,但是其可能会配置在http指令块中、server指令块或者location指令块。

为此我们修改上面的结构如下:

上面我们分析了http指令块内部的所有指令可能的存储格式,events指令块内部存储格式相比较简单很多,读者可以试着画一画。

那么这是否是nginx采用的存储格式呢?可以说和上图非常类似,nginx设计的配置存储格式见下图,这里暂时留两个疑问:

  • 1)如何实现http_ctx嵌套srv_ctx,srv_ctx嵌套loc_ctx;
  • 2)当某条指令同时出现在http指令块、server指令块和location指令块时,以哪个配置为准。

总结

本文作为nginx配置文件解析的第一小篇,简要介绍了nginx模块和指令的基本概念,同时针对http相关配置的存储格式进行了初步设计与讲解,为下文《nginx配置文件解析(二)》讲解。配置文件解析源码分析打下基础。

希望交流,一起学习Nginx PHP Redis 等源码的朋友请入微信群:

【Nginx源码分析】Nginx配置文件解析(一)相关推荐

  1. Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 一

    搭建nginx服务器时,主要的配置文件 nginx.conf 是部署和维护服务器人员经常要使用到的文件, 里面进行了许多服务器参数的设置.那么nginx 以模块 module为骨架的设计下是如何运用模 ...

  2. Nginx 源码分析

    1.工程 ngx_conf_file.c ngx_connection.c ngx_cycle.c ngx_file.h ngx_module.c ngx_open_file_cache.h ngx_ ...

  3. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

  4. Nginx源码分析:master/worker工作流程概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...

  5. Nginx源码分析:启动流程

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...

  6. nginx源码分析(5)——监听socket初始化

    在nginx源码分析(4)中,看到了nginx的事件模型,但其中没有介绍监听socket的初始化.而对于web server来说,需要通过监听socket来监听客户端的连接等.本篇将会具体介绍这方面的 ...

  7. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  8. Nginx源码分析之 upstream指令

    #Nginx 源码分析 upstream指令 想要的解决问题: 1:upstream存储结构 2:动态 upstream 流程(proxy_pass跟随变量或者域名) 最简单的配置文件 http {u ...

  9. Nginx源码分析:epoll事件处理模块概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 事件处理模块概述 Nginx的高效请求的处理依赖于事件管理机制,本次默认的场景是Linux操 ...

  10. Nginx源码分析:惊群处理与负载均衡

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的惊群处理与负载均衡概述 当Nginx工作在master/worker模式下时,就 ...

最新文章

  1. python学习--numpy的数组
  2. RDC如何打造支撑百万用户的分布式代码托管平台
  3. hdu4035 Maze 【期望dp + 数学】
  4. 趣谈设计模式 | 策略模式(Strategy):你还在使用冗长的if-else吗?
  5. java实现选择排序 带打印,选择排序算法的JAVA实现
  6. 渗透测试入门23之OSCP渗透测试认证经验分享
  7. 从尿检取中段谈数据库压测
  8. 1 恢复MySQL误删数据
  9. Android系统中用C语言来编写服务程序并且开机自启动运行服务
  10. 给控件做数字签名之二:生成证书文件
  11. CentOS7安装单机kubernetes和Docker
  12. 影院售票系统 php源码,影院售票系统
  13. Matlab画图常用命令
  14. 菜鸟AJAX 教程和JSON
  15. Box和Dropbox的区别
  16. mq使用replyto队列进行消息回复
  17. 为什么要参加hadoop培训
  18. 年龄识别之AgeNet
  19. 深拖式多道高分辨率地震探测系统
  20. 新iPhone 12泄漏

热门文章

  1. 用insert语句时,什么样的数据要加引号,什么样的数据不用加引号?
  2. H264 NAL单元简介
  3. mysqlskip-grant-tables
  4. MySQL常见的数据类型有哪些?
  5. 函数式编程中常用的函数(总结)
  6. 机器学习总结(十一):深度学习算法(CNN,SAE,等)及常见问题总结
  7. 推荐一款免费实用的报表工具
  8. 哈哥的博客阅读指南,一文对接全链路导引 --- 未完待续~
  9. Ubuntu终端配置oh-my-zsh(详细配置ohmyzsh步骤)
  10. Linux 项目实战记录