Nginx(六):配置解析之location解析

共 69905字,需浏览 140分钟

 ·

2021-01-24 04:28

走过路过不要错过

点击蓝字关注我们


nginx成为非常流行的代理服务软件,最根本的原因也许是在于其强悍性能。但还有一些必要的条件,比如功能的完整,配置的易用,能够解决各种各样的实际需求问题,这些是一个好的软件的必备特性。

那么,今天我们就来看看nginx配置的部分原则和解析原理吧。我们只做location部分的细节解析,但其他配置道理基本相通,推一及二即可。

1:nginx配置的基本原则

nginx是支持高度配置化的,那么也许就会涉及许多部分的配置,要如何协调好这些配置,是个问题。比如是否将配置定义一个个独立的文件,或者其他。

然而,nginx使用一个统一的配置文件,管理起了所有的配置工作。即 nginx.conf, 其默认位置是 $NGINX_HOME/nginx.conf, 在这个主配置文件中,又可以包含其他任意多的配置文件,从而达到统一管理的作用。

其默认配置nginx.conf如下:

#user  nobody;worker_processes  1;
#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;
#pid logs/nginx.pid;

events { worker_connections 1024;}

http { include mime.types; default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on; #tcp_nopush on;
#keepalive_timeout 0; keepalive_timeout 65;
#gzip on;
server { listen 80; server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / { root html; index index.html index.htm; }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; }
# proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #}
# deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }

# another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias;
# location / { # root html; # index index.html index.htm; # } #}

# HTTPS server # #server { # listen 443 ssl; # server_name localhost;
# ssl_certificate cert.pem; # ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on;
# location / { # root html; # index index.html index.htm; # } #}
}

这很明显不是什么标准语言语法,但其同样遵从一定的准则,从而让用户更易于理解配置。如:

    1. 每一个细配置项,都使用一个';'作为结束标识;
    2. '#' 代表该行被注释(这几乎是linux默认标准);
    3. 使用'{}'表示一个配置块,'{}'中代表其子项配置;
    4. 使用include可以包含其他文件的配置,相当于将其中的配置项copy到当前位置,该包含路径可以是相对路径也可以是绝对路径;

因此,基本上,你只要按照这个标准来做配置,至少语法 是不会有误了。但是,具体应该如何配置呢?实际上这要依赖于某类操作的具体实现,比如 location 的 配置, user 的配置,都是有各自的含义的。如果想要具体了解各细节配置,则必须要查询官网的配置定义了。请参考: https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/

总体上来说,nginx有几个顶级配置: 

    events – 连接类的配置,比如最大连接数配置
    http – 重头戏,http模块配置,所有的代理服务http服务都在其子配置下
    mail – 邮件配置
    stream – TCP、UDP 底层通信协议配置,功能与http模块相仿
一般地我们接接触最多的应该就是http配置了,至于其他功能,没有实践就没有发言权,略去不说。

2. location的配置用例

location的本义是用于定位一个http请求匹配情况,用于确定某个路径的请求应该如何做转换处理。它是属于 http 模块下的 server 模块下的一个选项配置。即 nginx -> http -> server -> location {..} 是其配置体现。它拥有相当多的配置项,因为做反向代理或其他服务器时,往往都可以通过这个配置,将功能完成。

如下几个配置项,可供参考:

http {    upstream backend {        ip_hash;        server backend1.example.com weight=5;        server backend2.example.com;        server 192.0.0.1 backup;    }    server {        root /www/data;        # 路径相等处理,优先级最高        location = / {            #...        }        # 根路径配置,优先级最低        location / {            root /data/www;        }        # 带前缀的配置,优先级其次        location /images/ {            root /data;        }        # 正则匹配的配置,优先级较高        location ~ \.(gif|jpg|png)$ {            root /data/images;        }        # 正则取反配置        location ^~ \.(php)$ {            root /data/other;        }        # 反向代理的配置,将请求转换给后端服务        # 如果backend是一个 upstream 配置,则做为一个负载均衡器使用        location /api1 {            proxy_set_header Host $host;            proxy_set_header X-Real-IP $remote_addr;            proxy_pass http://backend;        }        location /api2 {            proxy_pass http://www.abc.com;        }        # 路径重写        location /users/ {            rewrite ^/users/(.*)$ /show?user=$1 break;        }        # 带状态码的返回配置        location /wrong/url {            return 404;            open_file_cache_errors off;        }        location /permanently/moved/url {            return 301 http://www.example.com/moved/here;        }        # 响应内容替换        location / {            sub_filter     'href="http://127.0.0.1:8080/'    'href="https://$host/';            sub_filter     'img src="http://127.0.0.1:8080/' 'img src="https://$host/';            sub_filter_once on;        }    }}

更多内容可查阅官网: https://docs.nginx.com/nginx/admin-guide/web-server/web-server/

总体上来说,location提供了可以配置如何查找本地文件,以及可以配置如何转发请求到其他服务器的方式。其中,还有很多附加的设置各种需求变量的实现,以辅助我们实现一些正常请求提供的内容。配置比较多,到真正使用时,按需配置即可。一般也是一次配置,永久使用,不会太费事。

3. location配置的解析

nginx有自己的一套配置方法,那么这些配置好了的语句,如何应用到具体的服务上呢?自然是需要先进行解析,然后放置到对应的内存空间变量中,然后在需要的时候进行读取判定,以及转换了。大体思路如此,但如何解析配置却并非易事。因为我们的配置是无数现有配置的任意组合,如何有效的放置到可理解的位置,应该需要单独的数据结构设计,以及解析步骤。实际上,这也相当于是一个简单的编译器或解析器,它需要将文本解析为认识的东西。

下面我们就一起来看看nginx都是如何解析这些配置的吧!(这自然是在启动时完成的工作)

// 首先,nginx会解析启动行命令,这里面可以指定配置文件// core/nginx.c static ngx_int_tngx_get_options(int argc, char *const *argv){    u_char     *p;    ngx_int_t   i;
for (i = 1; i < argc; i++) {
p = (u_char *) argv[i];
if (*p++ != '-') { ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]); return NGX_ERROR; }
while (*p) {
switch (*p++) {
case '?': case 'h': ngx_show_version = 1; ngx_show_help = 1; break;
case 'v': ngx_show_version = 1; break;
case 'V': ngx_show_version = 1; ngx_show_configure = 1; break; // -t 测试配置文件有效性 case 't': ngx_test_config = 1; break;
case 'T': ngx_test_config = 1; ngx_dump_config = 1; break;
case 'q': ngx_quiet_mode = 1; break;
case 'p': if (*p) { ngx_prefix = p; goto next; }
if (argv[++i]) { ngx_prefix = (u_char *) argv[i]; goto next; }
ngx_log_stderr(0, "option \"-p\" requires directory name"); return NGX_ERROR; // -c nginx.conf 指定nginx配置文件路径 case 'c': if (*p) { ngx_conf_file = p; goto next; }
if (argv[++i]) { ngx_conf_file = (u_char *) argv[i]; goto next; }
ngx_log_stderr(0, "option \"-c\" requires file name"); return NGX_ERROR;
case 'g': if (*p) { ngx_conf_params = p; goto next; }
if (argv[++i]) { ngx_conf_params = (u_char *) argv[i]; goto next; }
ngx_log_stderr(0, "option \"-g\" requires parameter"); return NGX_ERROR; // -s (stop|quit|reopen|reload) 向现有运行的nginx进程发起控制命令 case 's': // 紧贴式给出命令: -sstop, -sreload if (*p) { ngx_signal = (char *) p;
} else if (argv[++i]) { ngx_signal = argv[i];
} else { ngx_log_stderr(0, "option \"-s\" requires parameter"); return NGX_ERROR; }
if (ngx_strcmp(ngx_signal, "stop") == 0 || ngx_strcmp(ngx_signal, "quit") == 0 || ngx_strcmp(ngx_signal, "reopen") == 0 || ngx_strcmp(ngx_signal, "reload") == 0) { ngx_process = NGX_PROCESS_SIGNALLER; goto next; }
ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal); return NGX_ERROR;
default: ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1)); return NGX_ERROR; } }
next:
continue; }
return NGX_OK;}

以上,是对命令行参数的简单解析,解析出来的变量放入到各全局变量中:ngx_show_version|ngx_show_help|ngx_show_configure|ngx_test_config|ngx_dump_config|ngx_quiet_mode|ngx_prefix|ngx_conf_file|ngx_conf_params|ngx_signal.

以上,最重要的是两个参数:-c -s, 用于指定配置文件和操作现有nginx进程。当然,对于配置解析,自然最重要的是 -c 命令了。但对于一些没有指定的配置值,则使用系统的默认值。其处理如下:

// core/nginx.cstatic ngx_int_tngx_process_options(ngx_cycle_t *cycle){    u_char  *p;    size_t   len;
if (ngx_prefix) { len = ngx_strlen(ngx_prefix); p = ngx_prefix;
if (len && !ngx_path_separator(p[len - 1])) { p = ngx_pnalloc(cycle->pool, len + 1); if (p == NULL) { return NGX_ERROR; }
ngx_memcpy(p, ngx_prefix, len); p[len++] = '/'; }
cycle->conf_prefix.len = len; cycle->conf_prefix.data = p; cycle->prefix.len = len; cycle->prefix.data = p;
} else {
#ifndef NGX_PREFIX
p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH); if (p == NULL) { return NGX_ERROR; }
if (ngx_getcwd(p, NGX_MAX_PATH) == 0) { ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed"); return NGX_ERROR; }
len = ngx_strlen(p);
p[len++] = '/';
cycle->conf_prefix.len = len; cycle->conf_prefix.data = p; cycle->prefix.len = len; cycle->prefix.data = p;
#else
#ifdef NGX_CONF_PREFIX // 默认路径前缀: conf/ ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);#else ngx_str_set(&cycle->conf_prefix, NGX_PREFIX);#endif ngx_str_set(&cycle->prefix, NGX_PREFIX);
#endif }
if (ngx_conf_file) { cycle->conf_file.len = ngx_strlen(ngx_conf_file); cycle->conf_file.data = ngx_conf_file;
} else { // 默认配置文件: conf/nginx.conf ngx_str_set(&cycle->conf_file, NGX_CONF_PATH); }
if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) { return NGX_ERROR; }
for (p = cycle->conf_file.data + cycle->conf_file.len - 1; p > cycle->conf_file.data; p--) { if (ngx_path_separator(*p)) { cycle->conf_prefix.len = p - cycle->conf_file.data + 1; cycle->conf_prefix.data = cycle->conf_file.data; break; } }
if (ngx_conf_params) { cycle->conf_param.len = ngx_strlen(ngx_conf_params); cycle->conf_param.data = ngx_conf_params; }
if (ngx_test_config) { cycle->log->log_level = NGX_LOG_INFO; }
return NGX_OK;}

真正的配置文件解析是在初始化cycle的时候处理实现的:

// core/ngx_cycle.cngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle){    void                *rv;    char               **senv;    ngx_uint_t           i, n;    ngx_log_t           *log;    ngx_time_t          *tp;    ngx_conf_t           conf;    ngx_pool_t          *pool;    ngx_cycle_t         *cycle, **old;    ngx_shm_zone_t      *shm_zone, *oshm_zone;    ngx_list_part_t     *part, *opart;    ngx_open_file_t     *file;    ngx_listening_t     *ls, *nls;    ngx_core_conf_t     *ccf, *old_ccf;    ngx_core_module_t   *module;    char                 hostname[NGX_MAXHOSTNAMELEN];
ngx_timezone_update();
/* force localtime update with a new timezone */
tp = ngx_timeofday(); tp->sec = 0;
ngx_time_update();

log = old_cycle->log;
pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); if (pool == NULL) { return NULL; } pool->log = log;
cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); if (cycle == NULL) { ngx_destroy_pool(pool); return NULL; }
cycle->pool = pool; cycle->log = log; cycle->old_cycle = old_cycle;
cycle->conf_prefix.len = old_cycle->conf_prefix.len; cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix); if (cycle->conf_prefix.data == NULL) { ngx_destroy_pool(pool); return NULL; }
cycle->prefix.len = old_cycle->prefix.len; cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix); if (cycle->prefix.data == NULL) { ngx_destroy_pool(pool); return NULL; }
cycle->conf_file.len = old_cycle->conf_file.len; cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1); if (cycle->conf_file.data == NULL) { ngx_destroy_pool(pool); return NULL; } ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data, old_cycle->conf_file.len + 1);
cycle->conf_param.len = old_cycle->conf_param.len; cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param); if (cycle->conf_param.data == NULL) { ngx_destroy_pool(pool); return NULL; }

n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;
if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; }
ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *));

if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; } // 使用红黑树存放配置信息 ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel, ngx_str_rbtree_insert_value); // 默认使用 20 个端口服务 if (old_cycle->open_files.part.nelts) { n = old_cycle->open_files.part.nelts; for (part = old_cycle->open_files.part.next; part; part = part->next) { n += part->nelts; }
} else { n = 20; } // 每个监听端口使用一个 open_files 表示 if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; }

if (old_cycle->shared_memory.part.nelts) { n = old_cycle->shared_memory.part.nelts; for (part = old_cycle->shared_memory.part.next; part; part = part->next) { n += part->nelts; }
} else { n = 1; }
if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; }
n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; }
ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t));

ngx_queue_init(&cycle->reusable_connections_queue);

cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); if (cycle->conf_ctx == NULL) { ngx_destroy_pool(pool); return NULL; }
// 获取当前机器的hostname if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed"); ngx_destroy_pool(pool); return NULL; }
/* on Linux gethostname() silently truncates name that does not fit */
hostname[NGX_MAXHOSTNAMELEN - 1] = '\0'; cycle->hostname.len = ngx_strlen(hostname);
cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len); if (cycle->hostname.data == NULL) { ngx_destroy_pool(pool); return NULL; }
ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);
// 创建module内存空间 if (ngx_cycle_modules(cycle) != NGX_OK) { ngx_destroy_pool(pool); return NULL; }
// 让各模块依次进行配置 for (i = 0; cycle->modules[i]; i++) { // NGINX_CORE_MODULE 可以进行配置文件处理 // 即几套顶级模块, http,events,mail,... if (cycle->modules[i]->type != NGX_CORE_MODULE) { continue; }
module = cycle->modules[i]->ctx; // 调用各模块的 create_conf 实现配置加载 // 其中,以为的http模块会进行解析,其实却没有 if (module->create_conf) { rv = module->create_conf(cycle); if (rv == NULL) { ngx_destroy_pool(pool); return NULL; } cycle->conf_ctx[cycle->modules[i]->index] = rv; } }

senv = environ;

ngx_memzero(&conf, sizeof(ngx_conf_t)); /* STUB: init array ? */ conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); if (conf.args == NULL) { ngx_destroy_pool(pool); return NULL; }
conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); if (conf.temp_pool == NULL) { ngx_destroy_pool(pool); return NULL; }

conf.ctx = cycle->conf_ctx; conf.cycle = cycle; conf.pool = pool; conf.log = log; conf.module_type = NGX_CORE_MODULE; conf.cmd_type = NGX_MAIN_CONF;
#if 0 log->log_level = NGX_LOG_DEBUG_ALL;#endif
if (ngx_conf_param(&conf) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; }
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; }
if (ngx_test_config && !ngx_quiet_mode) { ngx_log_stderr(0, "the configuration file %s syntax is ok", cycle->conf_file.data); }
for (i = 0; cycle->modules[i]; i++) { if (cycle->modules[i]->type != NGX_CORE_MODULE) { continue; }
module = cycle->modules[i]->ctx;
if (module->init_conf) { if (module->init_conf(cycle, cycle->conf_ctx[cycle->modules[i]->index]) == NGX_CONF_ERROR) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } } }
if (ngx_process == NGX_PROCESS_SIGNALLER) { return cycle; }
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
if (ngx_test_config) {
if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { goto failed; }
} else if (!ngx_is_init_cycle(old_cycle)) {
/* * we do not create the pid file in the first ngx_init_cycle() call * because we need to write the demonized process pid */
old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx, ngx_core_module); if (ccf->pid.len != old_ccf->pid.len || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0) { /* new pid file name */
if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { goto failed; }
ngx_delete_pidfile(old_cycle); } }

if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) { goto failed; }

if (ngx_create_paths(cycle, ccf->user) != NGX_OK) { goto failed; }

if (ngx_log_open_default(cycle) != NGX_OK) { goto failed; }
/* open the new files */
part = &cycle->open_files.part; file = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; file = part->elts; i = 0; }
if (file[i].name.len == 0) { continue; }
file[i].fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS);
ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, "log: %p %d \"%s\"", &file[i], file[i].fd, file[i].name.data);
if (file[i].fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, ngx_open_file_n " \"%s\" failed", file[i].name.data); goto failed; }
#if !(NGX_WIN32) if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fcntl(FD_CLOEXEC) \"%s\" failed", file[i].name.data); goto failed; }#endif }
cycle->log = &cycle->new_log; pool->log = &cycle->new_log;

/* create shared memory */
part = &cycle->shared_memory.part; shm_zone = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; shm_zone = part->elts; i = 0; }
if (shm_zone[i].shm.size == 0) { ngx_log_error(NGX_LOG_EMERG, log, 0, "zero size shared memory zone \"%V\"", &shm_zone[i].shm.name); goto failed; }
shm_zone[i].shm.log = cycle->log;
opart = &old_cycle->shared_memory.part; oshm_zone = opart->elts;
for (n = 0; /* void */ ; n++) {
if (n >= opart->nelts) { if (opart->next == NULL) { break; } opart = opart->next; oshm_zone = opart->elts; n = 0; }
if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) { continue; }
if (ngx_strncmp(shm_zone[i].shm.name.data, oshm_zone[n].shm.name.data, shm_zone[i].shm.name.len) != 0) { continue; }
if (shm_zone[i].tag == oshm_zone[n].tag && shm_zone[i].shm.size == oshm_zone[n].shm.size && !shm_zone[i].noreuse) { shm_zone[i].shm.addr = oshm_zone[n].shm.addr;#if (NGX_WIN32) shm_zone[i].shm.handle = oshm_zone[n].shm.handle;#endif
if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data) != NGX_OK) { goto failed; }
goto shm_zone_found; }
break; }
if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) { goto failed; }
if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) { goto failed; }
if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { goto failed; }
shm_zone_found:
continue; }

/* handle the listening sockets */
if (old_cycle->listening.nelts) { ls = old_cycle->listening.elts; for (i = 0; i < old_cycle->listening.nelts; i++) { ls[i].remain = 0; }
nls = cycle->listening.elts; for (n = 0; n < cycle->listening.nelts; n++) {
for (i = 0; i < old_cycle->listening.nelts; i++) { if (ls[i].ignore) { continue; }
if (ls[i].remain) { continue; }
if (ls[i].type != nls[n].type) { continue; }
if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen, ls[i].sockaddr, ls[i].socklen, 1) == NGX_OK) { nls[n].fd = ls[i].fd; nls[n].inherited = ls[i].inherited; nls[n].previous = &ls[i]; ls[i].remain = 1;
if (ls[i].backlog != nls[n].backlog) { nls[n].listen = 1; }
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
/* * FreeBSD, except the most recent versions, * could not remove accept filter */ nls[n].deferred_accept = ls[i].deferred_accept;
if (ls[i].accept_filter && nls[n].accept_filter) { if (ngx_strcmp(ls[i].accept_filter, nls[n].accept_filter) != 0) { nls[n].delete_deferred = 1; nls[n].add_deferred = 1; }
} else if (ls[i].accept_filter) { nls[n].delete_deferred = 1;
} else if (nls[n].accept_filter) { nls[n].add_deferred = 1; }#endif
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
if (ls[i].deferred_accept && !nls[n].deferred_accept) { nls[n].delete_deferred = 1;
} else if (ls[i].deferred_accept != nls[n].deferred_accept) { nls[n].add_deferred = 1; }#endif
#if (NGX_HAVE_REUSEPORT) if (nls[n].reuseport && !ls[i].reuseport) { nls[n].add_reuseport = 1; }#endif
break; } }
if (nls[n].fd == (ngx_socket_t) -1) { nls[n].open = 1;#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) if (nls[n].accept_filter) { nls[n].add_deferred = 1; }#endif#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) if (nls[n].deferred_accept) { nls[n].add_deferred = 1; }#endif } }
} else { ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { ls[i].open = 1;#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) if (ls[i].accept_filter) { ls[i].add_deferred = 1; }#endif#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) if (ls[i].deferred_accept) { ls[i].add_deferred = 1; }#endif } }
if (ngx_open_listening_sockets(cycle) != NGX_OK) { goto failed; }
if (!ngx_test_config) { ngx_configure_listening_sockets(cycle); }

/* commit the new cycle configuration */
if (!ngx_use_stderr) { (void) ngx_log_redirect_stderr(cycle); }
pool->log = cycle->log;
if (ngx_init_modules(cycle) != NGX_OK) { /* fatal */ exit(1); }

/* close and delete stuff that lefts from an old cycle */
/* free the unnecessary shared memory */
opart = &old_cycle->shared_memory.part; oshm_zone = opart->elts;
for (i = 0; /* void */ ; i++) {
if (i >= opart->nelts) { if (opart->next == NULL) { goto old_shm_zone_done; } opart = opart->next; oshm_zone = opart->elts; i = 0; }
part = &cycle->shared_memory.part; shm_zone = part->elts;
for (n = 0; /* void */ ; n++) {
if (n >= part->nelts) { if (part->next == NULL) { break; } part = part->next; shm_zone = part->elts; n = 0; }
if (oshm_zone[i].shm.name.len != shm_zone[n].shm.name.len) { continue; }
if (ngx_strncmp(oshm_zone[i].shm.name.data, shm_zone[n].shm.name.data, oshm_zone[i].shm.name.len) != 0) { continue; }
if (oshm_zone[i].tag == shm_zone[n].tag && oshm_zone[i].shm.size == shm_zone[n].shm.size && !oshm_zone[i].noreuse) { goto live_shm_zone; }
break; }
ngx_shm_free(&oshm_zone[i].shm);
live_shm_zone:
continue; }
old_shm_zone_done:

/* close the unnecessary listening sockets */
ls = old_cycle->listening.elts; for (i = 0; i < old_cycle->listening.nelts; i++) {
if (ls[i].remain || ls[i].fd == (ngx_socket_t) -1) { continue; }
if (ngx_close_socket(ls[i].fd) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_close_socket_n " listening socket on %V failed", &ls[i].addr_text); }
#if (NGX_HAVE_UNIX_DOMAIN)
if (ls[i].sockaddr->sa_family == AF_UNIX) { u_char *name;
name = ls[i].addr_text.data + sizeof("unix:") - 1;
ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "deleting socket %s", name);
if (ngx_delete_file(name) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_delete_file_n " %s failed", name); } }
#endif }

/* close the unnecessary open files */
part = &old_cycle->open_files.part; file = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; file = part->elts; i = 0; }
if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { continue; }
if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } }
ngx_destroy_pool(conf.temp_pool);
if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {
ngx_destroy_pool(old_cycle->pool); cycle->old_cycle = NULL;
return cycle; }

if (ngx_temp_pool == NULL) { ngx_temp_pool = ngx_create_pool(128, cycle->log); if (ngx_temp_pool == NULL) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "could not create ngx_temp_pool"); exit(1); }
n = 10;
if (ngx_array_init(&ngx_old_cycles, ngx_temp_pool, n, sizeof(ngx_cycle_t *)) != NGX_OK) { exit(1); }
ngx_memzero(ngx_old_cycles.elts, n * sizeof(ngx_cycle_t *));
ngx_cleaner_event.handler = ngx_clean_old_cycles; ngx_cleaner_event.log = cycle->log; ngx_cleaner_event.data = &dumb; dumb.fd = (ngx_socket_t) -1; }
ngx_temp_pool->log = cycle->log;
old = ngx_array_push(&ngx_old_cycles); if (old == NULL) { exit(1); } *old = old_cycle;
if (!ngx_cleaner_event.timer_set) { ngx_add_timer(&ngx_cleaner_event, 30000); ngx_cleaner_event.timer_set = 1; }
return cycle;

failed:
if (!ngx_is_init_cycle(old_cycle)) { old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx, ngx_core_module); if (old_ccf->environment) { environ = old_ccf->environment; } }
/* rollback the new cycle configuration */
part = &cycle->open_files.part; file = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; file = part->elts; i = 0; }
if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { continue; }
if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } }
/* free the newly created shared memory */
part = &cycle->shared_memory.part; shm_zone = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; shm_zone = part->elts; i = 0; }
if (shm_zone[i].shm.addr == NULL) { continue; }
opart = &old_cycle->shared_memory.part; oshm_zone = opart->elts;
for (n = 0; /* void */ ; n++) {
if (n >= opart->nelts) { if (opart->next == NULL) { break; } opart = opart->next; oshm_zone = opart->elts; n = 0; }
if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) { continue; }
if (ngx_strncmp(shm_zone[i].shm.name.data, oshm_zone[n].shm.name.data, shm_zone[i].shm.name.len) != 0) { continue; }
if (shm_zone[i].tag == oshm_zone[n].tag && shm_zone[i].shm.size == oshm_zone[n].shm.size && !shm_zone[i].noreuse) { goto old_shm_zone_found; }
break; }
ngx_shm_free(&shm_zone[i].shm);
old_shm_zone_found:
continue; }
if (ngx_test_config) { ngx_destroy_cycle_pools(&conf); return NULL; }
ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { if (ls[i].fd == (ngx_socket_t) -1 || !ls[i].open) { continue; }
if (ngx_close_socket(ls[i].fd) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } }
ngx_destroy_cycle_pools(&conf);
return NULL;}


nginx中设置了几个初始化的点,create_conf, init_conf, 供各模块实现各自的解析逻辑,以及使用一个全局的解析  ngx_conf_parse() 实现文件解析。

那么 http 作为独立的模块,其是否参与配置解析呢?我们看下其模块的配置即可:

// http 模块的配置简略// http/ngx_http.cstatic ngx_core_module_t  ngx_http_module_ctx = {    ngx_string("http"),    // 不做 create_conf 处理    NULL,    NULL};
ngx_module_t ngx_http_module = { NGX_MODULE_V1, &ngx_http_module_ctx, /* module context */ ngx_http_commands, /* module directives */ NGX_CORE_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING};

真正的解析工作在 ngx_conf_file.c 文件中进行, 有ngx_conf_param(), ngx_conf_parse() 完成具体的解析。

// core/ngx_conf_file.cchar *ngx_conf_param(ngx_conf_t *cf){    char             *rv;    ngx_str_t        *param;    ngx_buf_t         b;    ngx_conf_file_t   conf_file;
param = &cf->cycle->conf_param;
if (param->len == 0) { return NGX_CONF_OK; }
ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));
ngx_memzero(&b, sizeof(ngx_buf_t));
b.start = param->data; b.pos = param->data; b.last = param->data + param->len; b.end = b.last; b.temporary = 1;
conf_file.file.fd = NGX_INVALID_FILE; conf_file.file.name.data = NULL; conf_file.line = 0;
cf->conf_file = &conf_file; cf->conf_file->buffer = &b; // 解析param信息,不解析配置文件 rv = ngx_conf_parse(cf, NULL);
cf->conf_file = NULL;
return rv;}
char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename){ char *rv; ngx_fd_t fd; ngx_int_t rc; ngx_buf_t buf; ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, parse_param } type;
#if (NGX_SUPPRESS_WARN) fd = NGX_INVALID_FILE; prev = NULL;#endif // 如果给定配置文件,则解析 if (filename) {
/* open configuration file */ // 打开配置文件 fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, ngx_open_file_n " \"%s\" failed", filename->data); return NGX_CONF_ERROR; }
prev = cf->conf_file;
cf->conf_file = &conf_file;
if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); }
cf->conf_file->buffer = &buf; // NGX_CONF_BUFFER=1024 buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); if (buf.start == NULL) { goto failed; }
buf.pos = buf.start; buf.last = buf.start; buf.end = buf.last + NGX_CONF_BUFFER; buf.temporary = 1; // 初始化配置文件信息 cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; cf->conf_file->file.name.data = filename->data; cf->conf_file->file.offset = 0; cf->conf_file->file.log = cf->log; cf->conf_file->line = 1;
type = parse_file;
if (ngx_dump_config#if (NGX_DEBUG) || 1#endif ) { if (ngx_conf_add_dump(cf, filename) != NGX_OK) { goto failed; }
} else { cf->conf_file->dump = NULL; }
} else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
type = parse_block;
} else { type = parse_param; }
// 此处实现真正的解析操作 for ( ;; ) { // 重要1: 读取出一个个地token信息, 以pos, start等变量做标识起始 rc = ngx_conf_read_token(cf);
/* * ngx_conf_read_token() may return * * NGX_ERROR there is error * NGX_OK the token terminated by ";" was found * NGX_CONF_BLOCK_START the token terminated by "{" was found * NGX_CONF_BLOCK_DONE the "}" was found * NGX_CONF_FILE_DONE the configuration file is done */
if (rc == NGX_ERROR) { goto done; }
if (rc == NGX_CONF_BLOCK_DONE) {
if (type != parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); goto failed; }
goto done; }
if (rc == NGX_CONF_FILE_DONE) {
if (type == parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, expecting \"}\""); goto failed; }
goto done; }
if (rc == NGX_CONF_BLOCK_START) {
if (type == parse_param) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "block directives are not supported " "in -g option"); goto failed; } }
/* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
if (cf->handler) {
/* * the custom handler, i.e., that is used in the http's * "types { ... }" directive */
if (rc == NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\""); goto failed; }
rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; }
if (rv == NGX_CONF_ERROR) { goto failed; }
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", rv);
goto failed; }
     // 重要2: 将解析出的变量进行细致处理 rc = ngx_conf_handler(cf, rc);
if (rc == NGX_ERROR) { goto failed; } }
failed:
rc = NGX_ERROR;
done:
if (filename) { if (cf->conf_file->buffer->start) { ngx_free(cf->conf_file->buffer->start); }
if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", filename->data); rc = NGX_ERROR; }
cf->conf_file = prev; }
if (rc == NGX_ERROR) { return NGX_CONF_ERROR; }
return NGX_CONF_OK;}
// 读取一个个地 token 信息static ngx_int_tngx_conf_read_token(ngx_conf_t *cf){ u_char *start, ch, *src, *dst; off_t file_size; size_t len; ssize_t n, size; ngx_uint_t found, need_space, last_space, sharp_comment, variable; ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b, *dump;
found = 0; need_space = 0; last_space = 1; sharp_comment = 0; variable = 0; quoted = 0; s_quoted = 0; d_quoted = 0;
cf->args->nelts = 0; b = cf->conf_file->buffer; dump = cf->conf_file->dump; start = b->pos; start_line = cf->conf_file->line;
file_size = ngx_file_size(&cf->conf_file->file.info);
for ( ;; ) { // last默认是最大buffer,pos默认为0,所以以下逻辑不一定走 if (b->pos >= b->last) {
if (cf->conf_file->file.offset >= file_size) {
if (cf->args->nelts > 0 || !last_space) {
if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of parameter, " "expecting \";\""); return NGX_ERROR; }
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); return NGX_ERROR; }
return NGX_CONF_FILE_DONE; } // 默认两值相等,len为0 len = b->pos - start;
if (len == NGX_CONF_BUFFER) { cf->conf_file->line = start_line;
if (d_quoted) { ch = '"';
} else if (s_quoted) { ch = '\'';
} else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter \"%*s...\" started", 10, start); return NGX_ERROR; }
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter, probably " "missing terminating \"%c\" character", ch); return NGX_ERROR; }
if (len) { ngx_memmove(b->start, start, len); } // 计算剩余大小 size = (ssize_t) (file_size - cf->conf_file->file.offset); // 如果剩余大小超出当前缓冲的大小,则只能取当前缓冲剩余大小 if (size > b->end - (b->start + len)) { size = b->end - (b->start + len); } // 从配置文件中读取size大小的数据出来,放到buf中备用,相当于尽量一次全部读取 n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset);
if (n == NGX_ERROR) { return NGX_ERROR; }
if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } // 交换各偏移信息 b->pos = b->start + len; b->last = b->pos + n; start = b->start;
if (dump) { dump->last = ngx_cpymem(dump->last, b->pos, size); } } // 从文件中读出一个字符用以判定,即如果假设文件已读取完,则相当于一个个字符取出判定 ch = *b->pos++; // 遇到换行,line++,注释标识重置 if (ch == LF) { cf->conf_file->line++;
if (sharp_comment) { sharp_comment = 0; } } // 如果注释标识有效,则忽略当前段的字符,直到标识被清除 if (sharp_comment) { continue; } // 引号闭合处理 if (quoted) { quoted = 0; continue; } // 判断是否有新的语句开始,类似空格都可以 if (need_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { last_space = 1; need_space = 0; continue; } // 遇到分号,当前解析结束,即得到一个独立语句,如 root /www; if (ch == ';') { return NGX_OK; } // 遇到块也结束当前解析,交由子处理器处理 if (ch == '{') { return NGX_CONF_BLOCK_START; }
if (ch == ')') { last_space = 1; need_space = 0;
} else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } }
if (last_space) { // 新语句开始,行号暂存 start = b->pos - 1; start_line = cf->conf_file->line; // 忽略空格类字符 if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { continue; }
switch (ch) {
case ';': case '{': if (cf->args->nelts == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } // 语句结束,nelts 到底是啥? if (ch == '{') { return NGX_CONF_BLOCK_START; }
return NGX_OK;
case '}': if (cf->args->nelts != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); return NGX_ERROR; }
return NGX_CONF_BLOCK_DONE;
case '#': sharp_comment = 1; continue;
case '\\': // 转义符 quoted = 1; last_space = 0; continue;
case '"': start++; d_quoted = 1; last_space = 0; continue;
case '\'': start++; s_quoted = 1; last_space = 0; continue;
case '$': // 变量解析 variable = 1; last_space = 0; continue;
default: last_space = 0; }
} else { // ${xxx} 变量 if (ch == '{' && variable) { continue; }
variable = 0;
if (ch == '\\') { quoted = 1; continue; }
if (ch == '$') { variable = 1; continue; } // 引号处理 if (d_quoted) { if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; }
} else if (s_quoted) { if (ch == '\'') { s_quoted = 0; need_space = 1; found = 1; }
} else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';' || ch == '{') { last_space = 1; found = 1; // 完整解析一个语句 }
if (found) { word = ngx_array_push(cf->args); if (word == NULL) { return NGX_ERROR; }
word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1); if (word->data == NULL) { return NGX_ERROR; } // 将解析出的语句赋值到 args 中 for (dst = word->data, src = start, len = 0; src < b->pos - 1; len++) { if (*src == '\\') { switch (src[1]) { case '"': case '\'': case '\\': // 去除转义标识 src++; break;
case 't': // 翻译转义 *dst++ = '\t'; src += 2; continue;
case 'r': *dst++ = '\r'; src += 2; continue;
case 'n': *dst++ = '\n'; src += 2; continue; }
} *dst++ = *src++; } *dst = '\0'; word->len = len;
if (ch == ';') { return NGX_OK; }
if (ch == '{') { return NGX_CONF_BLOCK_START; }
found = 0; } } }}


大体原理是:读取一个个token,然后进行依次翻译,分小语句,大block,... 类型依次处理。将解析的结果放入cf->args中。其解析结果如 location ~ /api/.*go { ,如 listen 80 81;

还差一个细节,就是读取到token之后,又是如何传递给各模块的呢?实际就是上面的重点2:ngx_conf_handler() 处理。

static ngx_int_tngx_conf_handler(ngx_conf_t *cf, ngx_int_t last){    char           *rv;    void           *conf, **confp;    ngx_uint_t      i, found;    ngx_str_t      *name;    ngx_command_t  *cmd;    // 获取最后存入的配置变量名    name = cf->args->elts;
found = 0; // 遍历模块的 commands 列表,依次调用进行处理, 比如 root /www 处理 // location /api {..} 处理 for (i = 0; cf->cycle->modules[i]; i++) {
cmd = cf->cycle->modules[i]->commands; if (cmd == NULL) { continue; }
for ( /* void */ ; cmd->name.len; cmd++) { // 名字不同必然不负责解析 if (name->len != cmd->name.len) { continue; }
if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; }
found = 1; // 配置模块处理 if (cf->cycle->modules[i]->type != NGX_CONF_MODULE && cf->cycle->modules[i]->type != cf->module_type) { continue; }
/* is the directive's location right ? */ // 变量类型判定 if (!(cmd->type & cf->cmd_type)) { continue; }
if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" is not terminated by \";\"", name->data); return NGX_ERROR; }
if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" has no opening \"{\"", name->data); return NGX_ERROR; }
/* is the directive's argument count right ? */ // 参数个数匹配 if (!(cmd->type & NGX_CONF_ANY)) {
if (cmd->type & NGX_CONF_FLAG) {
if (cf->args->nelts != 2) { goto invalid; }
} else if (cmd->type & NGX_CONF_1MORE) {
if (cf->args->nelts < 2) { goto invalid; }
} else if (cmd->type & NGX_CONF_2MORE) {
if (cf->args->nelts < 3) { goto invalid; }
} else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {
goto invalid;
} else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } }
/* set up the directive's configuration context */
conf = NULL; // 找不同的配置级别位置存储(这指针用得6啊) if (cmd->type & NGX_DIRECT_CONF) { conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
} else if (cmd->type & NGX_MAIN_CONF) { conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
} else if (cf->ctx) { confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) { conf = confp[cf->cycle->modules[i]->ctx_index]; } } // 调用各命令的set方法,处理配置语义 // 实际上就涉及到内嵌套解析问题了 // 同样不会很简单哦 rv = cmd->set(cf, cmd, conf);
if (rv == NGX_CONF_OK) { return NGX_OK; }
if (rv == NGX_CONF_ERROR) { return NGX_ERROR; }
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive %s", name->data, rv);
return NGX_ERROR; } } // 配置使用错误 if (found) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive is not allowed here", name->data);
return NGX_ERROR; }
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown directive \"%s\"", name->data);
return NGX_ERROR;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of arguments in \"%s\" directive", name->data);
return NGX_ERROR;}

将解析出的配置信息接入到各子模块之后,才算是真正意义上的解析成功,也才能被各子模块使用。

下面我们再来看个样例,子模块如何解析配置,http location ....

listen 80; 的解析比较简单些,有兴趣可查看:

static char *ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){    ngx_http_core_srv_conf_t *cscf = conf;
ngx_str_t *value, size; ngx_url_t u; ngx_uint_t n; ngx_http_listen_opt_t lsopt;
cscf->listen = 1;
value = cf->args->elts;
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1]; u.listen = 1; u.default_port = 80;
if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in \"%V\" of the \"listen\" directive", u.err, &u.url); }
return NGX_CONF_ERROR; }
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
lsopt.backlog = NGX_LISTEN_BACKLOG; lsopt.rcvbuf = -1; lsopt.sndbuf = -1;#if (NGX_HAVE_SETFIB) lsopt.setfib = -1;#endif#if (NGX_HAVE_TCP_FASTOPEN) lsopt.fastopen = -1;#endif#if (NGX_HAVE_INET6) lsopt.ipv6only = 1;#endif // 更多参数解析,见官方文档说明 for (n = 2; n < cf->args->nelts; n++) {
if (ngx_strcmp(value[n].data, "default_server") == 0 || ngx_strcmp(value[n].data, "default") == 0) { lsopt.default_server = 1; continue; }
if (ngx_strcmp(value[n].data, "bind") == 0) { lsopt.set = 1; lsopt.bind = 1; continue; }
#if (NGX_HAVE_SETFIB) if (ngx_strncmp(value[n].data, "setfib=", 7) == 0) { lsopt.setfib = ngx_atoi(value[n].data + 7, value[n].len - 7); lsopt.set = 1; lsopt.bind = 1;
if (lsopt.setfib == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid setfib \"%V\"", &value[n]); return NGX_CONF_ERROR; }
continue; }#endif
#if (NGX_HAVE_TCP_FASTOPEN) if (ngx_strncmp(value[n].data, "fastopen=", 9) == 0) { lsopt.fastopen = ngx_atoi(value[n].data + 9, value[n].len - 9); lsopt.set = 1; lsopt.bind = 1;
if (lsopt.fastopen == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid fastopen \"%V\"", &value[n]); return NGX_CONF_ERROR; }
continue; }#endif
if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) { lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8); lsopt.set = 1; lsopt.bind = 1;
if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid backlog \"%V\"", &value[n]); return NGX_CONF_ERROR; }
continue; }
if (ngx_strncmp(value[n].data, "rcvbuf=", 7) == 0) { size.len = value[n].len - 7; size.data = value[n].data + 7;
lsopt.rcvbuf = ngx_parse_size(&size); lsopt.set = 1; lsopt.bind = 1;
if (lsopt.rcvbuf == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid rcvbuf \"%V\"", &value[n]); return NGX_CONF_ERROR; }
continue; }
if (ngx_strncmp(value[n].data, "sndbuf=", 7) == 0) { size.len = value[n].len - 7; size.data = value[n].data + 7;
lsopt.sndbuf = ngx_parse_size(&size); lsopt.set = 1; lsopt.bind = 1;
if (lsopt.sndbuf == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid sndbuf \"%V\"", &value[n]); return NGX_CONF_ERROR; }
continue; }
if (ngx_strncmp(value[n].data, "accept_filter=", 14) == 0) {#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) lsopt.accept_filter = (char *) &value[n].data[14]; lsopt.set = 1; lsopt.bind = 1;#else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "accept filters \"%V\" are not supported " "on this platform, ignored", &value[n]);#endif continue; }
if (ngx_strcmp(value[n].data, "deferred") == 0) {#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) lsopt.deferred_accept = 1; lsopt.set = 1; lsopt.bind = 1;#else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the deferred accept is not supported " "on this platform, ignored");#endif continue; }
if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) {#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) if (ngx_strcmp(&value[n].data[10], "n") == 0) { lsopt.ipv6only = 1;
} else if (ngx_strcmp(&value[n].data[10], "ff") == 0) { lsopt.ipv6only = 0;
} else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid ipv6only flags \"%s\"", &value[n].data[9]); return NGX_CONF_ERROR; }
lsopt.set = 1; lsopt.bind = 1;
continue;#else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "ipv6only is not supported " "on this platform"); return NGX_CONF_ERROR;#endif }
if (ngx_strcmp(value[n].data, "reuseport") == 0) {#if (NGX_HAVE_REUSEPORT) lsopt.reuseport = 1; lsopt.set = 1; lsopt.bind = 1;#else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "reuseport is not supported " "on this platform, ignored");#endif continue; }
if (ngx_strcmp(value[n].data, "ssl") == 0) {#if (NGX_HTTP_SSL) lsopt.ssl = 1; continue;#else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"ssl\" parameter requires " "ngx_http_ssl_module"); return NGX_CONF_ERROR;#endif }
if (ngx_strcmp(value[n].data, "http2") == 0) {#if (NGX_HTTP_V2) lsopt.http2 = 1; continue;#else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"http2\" parameter requires " "ngx_http_v2_module"); return NGX_CONF_ERROR;#endif }
if (ngx_strcmp(value[n].data, "spdy") == 0) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "invalid parameter \"spdy\": " "ngx_http_spdy_module was superseded " "by ngx_http_v2_module"); continue; }
if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {
if (ngx_strcmp(&value[n].data[13], "on") == 0) { lsopt.so_keepalive = 1;
} else if (ngx_strcmp(&value[n].data[13], "off") == 0) { lsopt.so_keepalive = 2;
} else {
#if (NGX_HAVE_KEEPALIVE_TUNABLE) u_char *p, *end; ngx_str_t s;
end = value[n].data + value[n].len; s.data = value[n].data + 13;
p = ngx_strlchr(s.data, end, ':'); if (p == NULL) { p = end; }
if (p > s.data) { s.len = p - s.data;
lsopt.tcp_keepidle = ngx_parse_time(&s, 1); if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) { goto invalid_so_keepalive; } }
s.data = (p < end) ? (p + 1) : end;
p = ngx_strlchr(s.data, end, ':'); if (p == NULL) { p = end; }
if (p > s.data) { s.len = p - s.data;
lsopt.tcp_keepintvl = ngx_parse_time(&s, 1); if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) { goto invalid_so_keepalive; } }
s.data = (p < end) ? (p + 1) : end;
if (s.data < end) { s.len = end - s.data;
lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len); if (lsopt.tcp_keepcnt == NGX_ERROR) { goto invalid_so_keepalive; } }
if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0 && lsopt.tcp_keepcnt == 0) { goto invalid_so_keepalive; }
lsopt.so_keepalive = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"so_keepalive\" parameter accepts " "only \"on\" or \"off\" on this platform"); return NGX_CONF_ERROR;
#endif }
lsopt.set = 1; lsopt.bind = 1;
continue;
#if (NGX_HAVE_KEEPALIVE_TUNABLE) invalid_so_keepalive:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid so_keepalive value: \"%s\"", &value[n].data[13]); return NGX_CONF_ERROR;#endif }
if (ngx_strcmp(value[n].data, "proxy_protocol") == 0) { lsopt.proxy_protocol = 1; continue; }
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[n]); return NGX_CONF_ERROR; } // 将监听端口加入列表 for (n = 0; n < u.naddrs; n++) { lsopt.sockaddr = u.addrs[n].sockaddr; lsopt.socklen = u.addrs[n].socklen; lsopt.addr_text = u.addrs[n].name; lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);
if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) { return NGX_CONF_ERROR; } }
return NGX_CONF_OK;}

location 的解析则肯定会复杂很多,因为它是一个块级的配置,内部将会有很多复杂的配置,想想这必然又涉及到递归解析了。

// location /xx {..} 的解析static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy){    char                      *rv;    u_char                    *mod;    size_t                     len;    ngx_str_t                 *value, *name;    ngx_uint_t                 i;    ngx_conf_t                 save;    ngx_http_module_t         *module;    ngx_http_conf_ctx_t       *ctx, *pctx;    ngx_http_core_loc_conf_t  *clcf, *pclcf;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; }
pctx = cf->ctx; ctx->main_conf = pctx->main_conf; ctx->srv_conf = pctx->srv_conf;
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->loc_conf == NULL) { return NGX_CONF_ERROR; } // 嵌套解析 for (i = 0; cf->cycle->modules[i]; i++) { if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) { continue; }
module = cf->cycle->modules[i]->ctx;
if (module->create_loc_conf) { // 将解析的值放入 ctx 中,备用 ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = module->create_loc_conf(cf); if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) { return NGX_CONF_ERROR; } } }
clcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; clcf->loc_conf = ctx->loc_conf;
value = cf->args->elts;
if (cf->args->nelts == 3) {
len = value[1].len; mod = value[1].data; name = &value[2]; // 疏松写法 // uri 模式匹配 if (len == 1 && mod[0] == '=') {
clcf->name = *name; clcf->exact_match = 1;
} else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
clcf->name = *name; clcf->noregex = 1;
} else if (len == 1 && mod[0] == '~') {
if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; }
} else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { return NGX_CONF_ERROR; }
} else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid location modifier \"%V\"", &value[1]); return NGX_CONF_ERROR; }
} else {
name = &value[1]; // 紧密写法 if (name->data[0] == '=') {
clcf->name.len = name->len - 1; clcf->name.data = name->data + 1; clcf->exact_match = 1;
} else if (name->data[0] == '^' && name->data[1] == '~') {
clcf->name.len = name->len - 2; clcf->name.data = name->data + 2; clcf->noregex = 1;
} else if (name->data[0] == '~') {
name->len--; name->data++;
if (name->data[0] == '*') {
name->len--; name->data++;
if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { return NGX_CONF_ERROR; }
} else { if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } }
} else {
clcf->name = *name;
if (name->data[0] == '@') { clcf->named = 1; } } }
pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
if (cf->cmd_type == NGX_HTTP_LOC_CONF) {
/* nested location */
#if 0 clcf->prev_location = pclcf;#endif
if (pclcf->exact_match) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" cannot be inside " "the exact location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; }
if (pclcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" cannot be inside " "the named location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; }
if (clcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "named location \"%V\" can be " "on the server level only", &clcf->name); return NGX_CONF_ERROR; }
len = pclcf->name.len;
#if (NGX_PCRE) if (clcf->regex == NULL && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)#else if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)#endif { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" is outside location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; } } // 添加到location列表中,备用 if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) { return NGX_CONF_ERROR; }
save = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_LOC_CONF; // 递归解析 rv = ngx_conf_parse(cf, NULL);
*cf = save;
return rv;}

同样,它会遍历其http模块下有哪些可以解析的命令,然后交其处理,然后再处理自身的逻辑。从而完成整个块的配置解析。

4. location配置的应用

上一节已经解析出location的各项配置了,那么它是如何运用到实际中呢?实际上,就是在需要的时候,从相应配置变量中取出来使用判定即可。




往期精彩推荐



腾讯、阿里、滴滴后台面试题汇总总结 — (含答案)

面试:史上最全多线程面试题 !

最新阿里内推Java后端面试题

JVM难学?那是因为你没认真看完这篇文章


END


关注作者微信公众号 —《JAVA烂猪皮》


了解更多java后端架构知识以及最新面试宝典


你点的每个好看,我都认真当成了


看完本文记得给作者点赞+在看哦~~~大家的支持,是作者源源不断出文的动力


作者:等你归去来

出处:https://www.cnblogs.com/yougewe/p/14289846.html

浏览 8
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报