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

浏览 901

概述

这几篇博客文章我们将会重点分析nginx配置项解析的原理。因为nginx配置框架设计的非常灵活和强大,这就使得我们在分析其内部机制的时候带来了不小的挑战,而这个系列的博客的意义就在于梳理其内部实现,并整理出大致框架,以分享给读者。

作者之前看过网上的一些博客写的关于nginx配置框架的梳理,粗制滥造,因此萌生了自己写的想法。希望大家能提出宝贵意见。

在讲配置之前,我们不得不简单说下nginx模块的概念。因为nginx属于微内核设计,nginx的强大之处在于灵活的微内核再加上可扩展的模块,nginx自身的模块有core、event、http、mail等核心模块,但是开发者又可以基于这些核心模块开发满足自身业务需求的模块,首当其冲的便是http模块了,因此,大家在阅读nginx代码的时候可以看到很多的ngx_http_xxx,这些都是基于http模块开发的第三方模块,而我们后面局里的重点也将会是http,谁叫他这么重要呢。

好了,言归正传,我们来看看nginx如何管理复杂的配置项吧。我在这里假设各位看官对nginx配置项的形式有了初步的了解,如果还没有配置过nginx的,那么请先移步,自行脑补再回头。

我们在本篇博客中会详细描述nginx配置解析的源头,从头干起,可避免在后面解析的时候的突兀感。

nginx的配置项的老祖宗就藏在下面这个数据结构了

  1. struct ngx_cycle_s {
  2. void ****conf_ctx;
  3. ......
  4. }

当时看到这个结构的时候,我承认我有种淡淡的忧伤,我自认c语言也还不错,但是还是花了点时间才整明白这个四重指针到底是怎么指来指去的。而这个也是我们拨云见雾的第一步,我们可以并且必须搞清楚这鸟玩意到底是干嘛的。

首先看看它是如何诞生的:

  1. <pre name="code" class="cpp">ngx_cycle_t *
  2. ngx_init_cycle(ngx_cycle_t *old_cycle)
  3. {

cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); if (cycle->conf_ctx == NULL) { ngx_destroy_pool(pool); return NULL; } ......}从这里我们看以看到,conf_ctx是一个指针数组,数组一共有ngx_max_module项(也就是nginx有多少个模块数组就有多大)。好了,除此之外,我们看不出更多信息。所以目前为止我们看到的结构如下:

那么接下来我们的任务就是弄清楚这个void*数组里面的每一项到底指向了谁

  1. for (i = 0; ngx_modules[i]; i++) {
  2. if (ngx_modules[i]->type != NGX_CORE_MODULE) {
  3. continue;
  4. }
  5. module = ngx_modules[i]->ctx;
  6. if (module->create_conf) {
  7. rv = module->create_conf(cycle);
  8. if (rv == NULL) {
  9. ngx_destroy_pool(pool);
  10. return NULL;
  11. }
  12. cycle->conf_ctx[ngx_modules[i]->index] = rv;
  13. }
  14. }
  15. ......
  16. }

在这里,会调用每个模块的create_conf()方法,将结果保存在数组的对应项中。如core模块的ngx_core_module_create_conf(),然而对于大部分模块来说,其实并没有实现该方法,如我们关注的http_core模块,因此http_core模块对应的该项其实还是为NULL。此时数据结构图如下:


前面说过,对nginx的配置解析我们以http模块为例来说明,因此,接下来我们就来看看http模块的配置是如何一步步的被加载的。

在完成了配置数据结构的初始化以后,接下来就进入配置解析阶段了,我们不关心nginx是如何回调各模块的配置解析方法。我们就来看看nginx如何解析http配置。

首先,在nginx的配置设计中http模块的配置项位于下面的block

  1. http{
  2. ......
  3. }

nginx遇到"http"指令时,会调用该指令的解析函数ngx_http_block

  1. static char *
  2. ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  3. {
  4. ...
  5. /* the main http context */
  6. ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
  7. if (ctx == NULL) {
  8. return NGX_CONF_ERROR;
  9. }
  10. *(ngx_http_conf_ctx_t **) conf = ctx;
  11. /* count the number of the http modules and set up their indices */
  12. ngx_http_max_module = 0;
  13. for (m = 0; ngx_modules[m]; m++) {
  14. if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
  15. continue;
  16. }
  17. ngx_modules[m]->ctx_index = ngx_http_max_module++;
  18. }
  19. ......
  20. }

上面函数有一句非常重要

  1. ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
  2. *(ngx_http_conf_ctx_t **) conf = ctx;

首先为http模块分配一个ngx_http_conf_ctx_t结构,然后再将该结构存储在conf指针指向的内存处,而追溯conf指针的来源是ngx_conf_handler():

  1. if (cmd->type & NGX_DIRECT_CONF) {
  2. conf = ((void **) cf->ctx)[ngx_modules[i]->index];
  3. } else if (cmd->type & NGX_MAIN_CONF) {
  4. conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
  5. // 对于http 块内的解析会进入该分支
  6. } else if (cf->ctx) {
  7. confp = *(void **) ((char *) cf->ctx + cmd->conf);
  8. if (confp) {
  9. conf = confp[ngx_modules[i]->ctx_index];
  10. }
  11. }
  12. rv = cmd->set(cf, cmd, conf);

由于"http"是http_core模块的指令,cmd->type满足NGX__MAIN_CONF,因此这里的conf其实就代表了cycle->conf_ctx[ngx_http_core_module]这项的地址,而

*(ngx_http_conf_ctx_t **) cof = ctx就是将ctx(指向ngx_http_conf_ctx结构的指针)放在数组的这一项里面,此时形成的结构如下图:

而接下来又会初始化该ngx_http_conf_ctx_t这个结构

  1. ctx->main_conf = ngx_pcalloc(cf->pool,
  2. sizeof(void *) * ngx_http_max_module);
  3. ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
  4. ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

形成的结构如下:

我想,话说到这个份上,大家应该能理解为什么cycle_conf_ctx是四重指针了吧。

已修改于2023-03-05 02:19
创作不易,留下一份鼓励
皮皮鲁

暂无个人介绍

关注



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

按点赞数排序

按时间排序

关于作者
皮皮鲁
这家伙很懒还未留下介绍~
85
文章
2
问答
41
粉丝
相关文章
我们比较了社区版 Ingresser Controller、NGINX 开源版 Ingress Controller 和 NGINX Plus Ingress Controller 在动态 Kubernetes 云环境中的性能。经过测试,我们得出:只有 NGINX Plus Ingress Controller 不会随着 Pod 副本数量的增加和减少而产生高延迟。访问 NGINX 中文官方开源社区(nginx.org.cn)了解详情。
点赞 1
浏览 1.3k
原文作者:Jason Schmidt of F5原文链接:在 Kubernetes 中实现自助 DNS 和证书管理转载来源:NGINX 官方网站NGINX唯一中文官方社区 ,尽在 nginx.org.
点赞 0
浏览 643
nginx可以通过信号进行控制。主进程的进程ID默认情况下被写入文件/usr/local/nginx/logs/nginx.pid。此名称可以在配置时更改,或在nginx.conf中使用pid指令进行更改。主进程支持以下信号:
点赞 0
浏览 418