设为首页 收藏本站
查看: 650|回复: 0

[经验分享] Nginx配置文件解析之一

[复制链接]

尚未签到

发表于 2016-12-25 07:43:50 | 显示全部楼层 |阅读模式
  现在针对nginx源码分析的blog和文章已经很多了,之前我也看过不少,大家的分析都很不错。太多重复的内容就不写了,主要想针对在我分析代码和查阅blog的过程中,发现的一些比较晦涩或者某些细节有待展开讨论的地方,给出我的自己理解和看法,希望跟大家交流和学习。
  使用的nginx版本是nginx-1.0.6,我最开始看的代码是0.7.62,新的版本在功能和稳定性上做了很多的工作。
  在分析的时候,我尽量简单明了,不太重要的地方一带而过,具体地大家可以去读代码。相对复杂或者晦涩的地方,将详细展开。
  首先我们从配置文件开始,下面的分析是建立在网友对nginx的配置文件结构有大概熟悉为前提,这样才可以很好的理解代码。
  这里有必要提醒一点:原始代码目录中ngx_modules这个结构,是找不到它的定义和初始化,要看到它,你必须执行configure,make,在原来的代码目录下会出现一个objs文件夹,里面的3个文件ngx_auto_config.h,ngx_auto_headers.h,ngx_modules.c,需要在建source insight工程时也包含进去,这样有利于我们把握整个代码结构。呵呵,有意思的是,nginx的configure文件是作者手工写的,里面有许多管理代码工程的方法,有时间的话,也是值得学习下的。
  1.ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);
  配置文件的解析相关的处理主要在ngx_init_cycle函数中被调用。既然如此,我们就先说说ngx_init_cycle函数吧。
  它需要一个参数类型为ngx_cycle_t *,返回值也是一个ngx_cycle_t*,与此同时我们注意到参数名为old_cycle,那么这个函数的作用是啥呢?很明显是由old得到一个new。其中ngx_cycle_t的结构保存一些全局的配置和信息。这个函数具体作用将在reconfig(重读配置文件)的时候得到体现,可以理解为old_cycle是当前正在使用的配置信息,当配置文件做了某些修改之后,ngx_init_cycle通过old_cycle中的一些数据,对new_cycle进行一些设置,在经过进一步的配置解析之后,就可以得到一个newcycle。
  2.char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
  当我们使用sourceinsight查看这个函数的调用情况时,会发现调用它的地方很多。其实,入口点就在ngx_init_cycle中对ngx_conf_parse调用,后面的所有的调用可以看作是在此之后的递归调用。为什么会是这个样子呢?原因在于nginx是一边读取配置信息,一边解析执行相关的处理,具体一点讲,就是“读一行,执行一行”,一行的定义在这里是指以分号或者是“{”和“}”等结尾的一行,例如:我们解析到http {},我们就调用针对httpblock的处理,在处理的时候我们又会碰到server{},自然就会调用server block的处理。。。以此类推!。
  对配置文件的读取主要在函数ngx_conf_read_token中,这个函数每次会把NGX_CONF_BUFFER(即4KB)大小的配置信息读到内存buf中,然后对该buf进行分析。在该函数外,主要通过该函数的返回值在做不同的处理,关于它的返回值的含义,大家可以去细细读下代码,意义很明确!
  配置指令我们通过上面的操作就可以拿到了,以空格分开的各个字符串被保存在一个字符串数组里(即cf->args),这一点代码体现的很明显。之后呢,我们就调用ngx_conf_handler函数来处理当前拿到的这行配置。这里我们先从总体上说一下ngx_conf_handler这个函数的工作原理:”它遍历系统中所有的模块配置,找到特定模块,并匹配特定命令,然后执行“。下面我们展开来讲一下。
  nginx会将所有的模块分类管理,自然各个模块被划分到了一个个“集合”中去,同样一个模块下的指令也是分类的(如属于哪类模块,配置在哪些位置是正确的等等),所以在每次调用ngx_conf_parse的时候都会指出”我这次解析的配置信息是啥类型“,举例:
  conf.module_type= NGX_CORE_MODULE;
conf.cmd_type = NGX_MAIN_CONF;
  即解析得到的指令我们将core module类型中查找,并且在找到的module中类型为main conf的指令。
  在进入重点之前,我们先看一个地方,就是cf->handler的处理,它是做什么用的呢?是这样的,nginx的通用处理函数ngx_conf_handler主要是针对cf->args的字符串数组来使用的,像有的配置,如types,charset_map,并不是单纯的字符串数组,指令的参数可能会放在{}中,这样通用的解析和处理函数就不适用了。通过我们注册cf->handler,我们就可以对下面{}中参数做常规的配置处理了。
  我们进入到ngx_conf_handler函数中,看看它的工作机理。
  3. static ngx_int_t
  ngx_conf_handler(ngx_conf_t *cf,ngx_int_t last)
  参数last是ngx_conf_read_token解析的返回结果。强调一点的是,cf->args中已经保存了我们需要的各个参数。
  接下来的处理分4步走:
  (1) 模块匹配
  代码显示的很明显,它首先会根据你指定的类型,对特定的模块进行查找。
  (2) command匹配
  找到匹配的模块之后会遍历该模块下的command数组,来从中找到它需要找command信息。如果字面上匹配,还要进行command类型的配置检查,如果此时类型不匹配,nginx会报告给你。
  (3) 参数个数匹配
  主要检查对个数有严格要求的command,对于任意个数(即NGX_CONF_ANY)的command,就直接处理了。
  关于参数个数,nginx定义了一些宏,如NGX_CONF_TAKEx(x代表1,2,3等,表示可配置一个,两个,三个参数。。),还有像1MORE,2MORE,意思也很明确。注意的是,对于有个数限制的command,最多可配置的个数为8个,即NGX_CONF_MAX_ARGS,这个在写自己的module时要注意,我吃过这方面的亏。。。
  (4) 执行command(即set函数)。
  好了,前面的检查工作做完之后,就要真正去执行相关的操作,生成module的配置信息了。
  这里涉及command的类型问题,搞清楚它对后面的理解很重要。Let’s go!
  NGX_DIRECT_CONF,NGX_MAIN_CONF,NGX_HTTP_MAIN_CONF,NGX_HTTP_SRV_CONF,NGX_HTTP_LOC_CONF等
  其中NGX_DIRECT_CONF一般是在http块等之外的配置, NGX_HTTP_MAIN_CONF是直接配置在http块中的,NGX_HTTP_SRV_CONF配置在server块中,NGX_HTTP_LOC_CONF配置在location中等等。
  我们来看cf->ctx这个成员,它的类型是void *,到这里你大概会猜到它的用处,肯定是在不同的条件下转来转去!
  在ngx_init_cycle中我们发现最初cf->ctx的值由cycle->conf_ctx赋值得到,cycle->conf_ctx是一个void ****类型。(-_-!)
  在ngx_init_cycle中有这样关键的一句:
  cycle->conf_ctx =ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
  所以,这里ngx_max_module是系统中所有module的总数,conf_ctx在这一句中用来给每个module占一个位置(一个指针),但是每个位置到底指向啥东西呢?反正是个void *,指啥都行,一般都只指向一些模块的配置结构,但是对于有些模块,我们要拿到它,要透过n层的指针(例如void ****)来拿到,这样的设计是为了逻辑分层和模块划分。
  兜了一圈,我们发现最终这些指针数组的成员赋值都会在这里的第4步来完成:
  这里分3种情况:
  (1) NGX_DIRECT_CONF
  对于那些游离于{}之外的配置,一般属于ngx_core_conf_t的配置内容,在ngx_init_cycle的时候已经对NGX_CORE_MODULE类型的模块进行了初始化(模块需要有init函数),这里根据配置信息,set函数会做配置结构内中成员的赋值。
  (2) NGX_MAIN_CONF
  这样的配置包括event,http等,他们没有init函数,所以实际的空间分配需要在set函数内完成,于是就有了:
  conf =&(((void **) cf->ctx)[ngx_modules->index]); //取指针的地址
  rv =cmd->set(cf, cmd, conf); // 指针在函数内被赋值
  set中conf参数是一个二重指针,这也就有个之后在ngx_http_block中的语句:
  ctx =ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
  *(ngx_http_conf_ctx_t **) conf = ctx;
  (3)其他
  其实这里的“其他”,主要是一些server和location的类型的command,这些command大量的集中于http的相关配置中。
  我们先来看ngx_http_conf_ctx_t结构:
  typedefstruct {
  void**main_conf;
  void**srv_conf;
  void**loc_conf;
  }ngx_http_conf_ctx_t;
  这里有一点需要交代的是,像http模块下,有一些所谓的子模块,而这些子模块基本上都是http中server或者location中的一些配置。这些配置通过模块中ctx_index,以数组的形式,将他们的配置结构的指针保存在srv_conf或loc_conf中,这就是他们的类型为什么会是void **。
  看下面的行代码:
  cmd->conf是cf->ctx成员的位移,这里的处理就是把该位置的成员当成一个指针来处理,而实际上,它是一个二级指针,即子模块配置的指针的数组!
  confp =*(void **) ((char *) cf->ctx + cmd->conf);
  conf = confp[ngx_modules->ctx_index]; // 拿到配置结构
  rv =cmd->set(cf, cmd, conf); // 做处理
  最后说明一下,开始提到的那个void ****类型成员,使用这种类型的原因我们可以在ngx_events_block函数中找到答案。
  (未完待续)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-318931-1-1.html 上篇帖子: 了解 Nginx 的 7 个原因(转) 下篇帖子: Nginx基本配置、性能优化指南
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表