Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >nginx0.1.0之http模块初始化源码分析(4)

nginx0.1.0之http模块初始化源码分析(4)

作者头像
theanarkh
发布于 2019-03-06 01:55:15
发布于 2019-03-06 01:55:15
42400
代码可运行
举报
文章被收录于专栏:原创分享原创分享
运行总次数:0
代码可运行

我们继续分析ngx_http_block函数剩余的代码,剩下的代码就是处理phases和监听的端口、地址、servername的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 恢复上下文
    *cf = pcf;

    /* init lists of the handlers */

    ngx_init_array(cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
                   cf->cycle->pool, 10, sizeof(ngx_http_handler_pt),
                   NGX_CONF_ERROR);
    cmcf->phases[NGX_HTTP_REWRITE_PHASE].type = NGX_OK;


    /* the special find config phase for single handler */

    ngx_init_array(cmcf->phases[NGX_HTTP_FIND_CONFIG_PHASE].handlers,
                   cf->cycle->pool, 1, sizeof(ngx_http_handler_pt),
                   NGX_CONF_ERROR);
    cmcf->phases[NGX_HTTP_FIND_CONFIG_PHASE].type = NGX_OK;

    ngx_test_null(h, ngx_push_array(
                           &cmcf->phases[NGX_HTTP_FIND_CONFIG_PHASE].handlers),
                  NGX_CONF_ERROR);
    *h = ngx_http_find_location_config;


    ngx_init_array(cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
                   cf->cycle->pool, 10, sizeof(ngx_http_handler_pt),
                   NGX_CONF_ERROR);
    cmcf->phases[NGX_HTTP_ACCESS_PHASE].type = NGX_DECLINED;


    ngx_init_array(cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
                   cf->cycle->pool, 10, sizeof(ngx_http_handler_pt),
                   NGX_CONF_ERROR);
    cmcf->phases[NGX_HTTP_CONTENT_PHASE].type = NGX_OK;


    /*
     * create the lists of the ports, the addresses and the server names
     * to allow quickly find the server core module configuration at run-time
     */

    ngx_init_array(in_ports, cf->pool, 10, sizeof(ngx_http_in_port_t),
                   NGX_CONF_ERROR);

    /* "server" directives */
    cscfp = cmcf->servers.elts;
    // 遍历每个server下的每个listen结构的每个端口,构造端口->地址->servername的多级结构
    for (s = 0; s < cmcf->servers.nelts; s++) {

        /* "listen" directives */
        // 在解析listen指令的时候写入
        lscf = cscfp[s]->listen.elts;
        for (l = 0; l < cscfp[s]->listen.nelts; l++) {

            port_found = 0;

            /* AF_INET only */

            in_port = in_ports.elts;
            for (p = 0; p < in_ports.nelts; p++) {

                if (lscf[l].port == in_port[p].port) {
                    // 端口已经在监听
                    /* the port is already in the port list */

                    port_found = 1;
                    addr_found = 0;
                    // 监听了这个端口的地址列表
                    in_addr = in_port[p].addrs.elts;
                    for (a = 0; a < in_port[p].addrs.nelts; a++) {
                        // 监听的端口相等,判断地址是否也相等
                        if (lscf[l].addr == in_addr[a].addr) {

                            /* the address is already bound to this port */

                            /* "server_name" directives */
                            // server对应的servername列表
                            s_name = cscfp[s]->server_names.elts;
                            for (n = 0; n < cscfp[s]->server_names.nelts; n++) {

                                /*
                                 * add the server name and server core module
                                 * configuration to the address:port
                                 */

                                /* TODO: duplicate names can be checked here */

                                ngx_test_null(name,
                                              ngx_push_array(&in_addr[a].names),
                                              NGX_CONF_ERROR);

                                name->name = s_name[n].name;
                                name->core_srv_conf = s_name[n].core_srv_conf;
                            }

                            /*
                             * check duplicate "default" server that
                             * serves this address:port
                             */
                            // 同一个server配置里只能有一个default_server
                            if (lscf[l].default_server) {
                                if (in_addr[a].default_server) {
                                    ngx_log_error(NGX_LOG_ERR, cf->log, 0,
                                           "duplicate default server in %s:%d",
                                           lscf[l].file_name.data,
                                           lscf[l].line);

                                    return NGX_CONF_ERROR;
                                }

                                in_addr[a].core_srv_conf = cscfp[s];
                                in_addr[a].default_server = 1;
                            }

                            addr_found = 1;

                            break;

                        } else if (in_addr[a].addr == INADDR_ANY) {

                            /*
                             * "*:port" must be the last resort so move it
                             * to the end of the address list and add
                             * the new address at its place
                             */
                            /*
                                如果监听的地址是any,则把any的项放到地址列表的最后,
                                原来的位置填充当前的listen配置的信息,遍历到any项说明
                                已经是地址列表的最后一项
                            */
                            ngx_test_null(inaddr,
                                          ngx_push_array(&in_port[p].addrs),
                                          NGX_CONF_ERROR);

                            ngx_memcpy(inaddr, &in_addr[a],
                                       sizeof(ngx_http_in_addr_t));

                            in_addr[a].addr = lscf[l].addr;
                            in_addr[a].default_server = lscf[l].default_server;
                            in_addr[a].core_srv_conf = cscfp[s];

                            /*
                             * create the empty list of the server names that
                             * can be served on this address:port
                             */

                            ngx_init_array(inaddr->names, cf->pool, 10,
                                           sizeof(ngx_http_server_name_t),
                                           NGX_CONF_ERROR);
                            // 置1,下面不需要再新增一个项
                            addr_found = 1;
                            // 跳出循环,因为已经遍历到了any项了,说明是最后一项了
                            break;
                        }
                    }
                    // 还没有该地址的项,新增一个
                    if (!addr_found) {

                        /*
                         * add the address to the addresses list that
                         * bound to this port
                         */

                        ngx_test_null(inaddr,
                                      ngx_push_array(&in_port[p].addrs),
                                      NGX_CONF_ERROR);

                        inaddr->addr = lscf[l].addr;
                        inaddr->default_server = lscf[l].default_server;
                        inaddr->core_srv_conf = cscfp[s];

                        /*
                         * create the empty list of the server names that
                         * can be served on this address:port
                         */

                        ngx_init_array(inaddr->names, cf->pool, 10,
                                       sizeof(ngx_http_server_name_t),
                                       NGX_CONF_ERROR);
                    }
                }
            }
            // 在已监听端口里没有找到当前端口
            if (!port_found) {

                /* add the port to the in_port list */
                // 记录该端口
                ngx_test_null(in_port,
                              ngx_push_array(&in_ports),
                              NGX_CONF_ERROR);

                in_port->port = lscf[l].port;

                ngx_test_null(in_port->port_text.data, ngx_palloc(cf->pool, 7),
                              NGX_CONF_ERROR);
                in_port->port_text.len = ngx_snprintf((char *)
                                                      in_port->port_text.data,
                                                      7, ":%d",
                                                      in_port->port);

                /* create list of the addresses that bound to this port ... */
                // 监听了这个端口的地址列表
                ngx_init_array(in_port->addrs, cf->pool, 10,
                               sizeof(ngx_http_in_addr_t),
                               NGX_CONF_ERROR);

                ngx_test_null(inaddr, ngx_push_array(&in_port->addrs),
                              NGX_CONF_ERROR);

                /* ... and add the address to this list */

                inaddr->addr = lscf[l].addr;
                inaddr->default_server = lscf[l].default_server;
                // 记录server配置,方便后续快速查找
                inaddr->core_srv_conf = cscfp[s];

                /*
                 * create the empty list of the server names that
                 * can be served on this address:port
                 */

                ngx_init_array(inaddr->names, cf->pool, 10,
                               sizeof(ngx_http_server_name_t),
                               NGX_CONF_ERROR);
            }
        }
    }
    // 代码执行到这,内存视图对应下图的图一(圆圈里是1)。
    /* optimize the lists of the ports, the addresses and the server names */

    /* AF_INET only */
    // 优化,如果不存在虚拟主机则不需要保存servername,因为servername是用来匹配虚拟主机的
    // 监听的端口列表
    in_port = in_ports.elts;
    for (p = 0; p < in_ports.nelts; p++) {

        /* check whether the all server names point to the same server */
        // 地址列表
        in_addr = in_port[p].addrs.elts;
        for (a = 0; a < in_port[p].addrs.nelts; a++) {

            virtual_names = 0;
            // servername列表
            name = in_addr[a].names.elts;
            for (n = 0; n < in_addr[a].names.nelts; n++) {
                /*
                    比较当前的地址和该地址下所有的servername,如果指向的server配置
                    是一样的,说明当前的地址收到连接或者请求的时候,直接给server里配置的服务
                    就行,如果有一个servername指向的server配置和当前地址的不一样,说明当一个请求或
                    连接到来时,nginx无法通过地址决定该请求转发给哪个服务,还需要servername信息,即
                    虚拟主机名
                */
                if (in_addr[a].core_srv_conf != name[n].core_srv_conf) {
                    virtual_names = 1;
                    break;
                }
            }

            /*
             * if the all server names point to the same server
             * then we do not need to check them at run-time
             */
            // 没有配置虚拟主机则不需要保存servername信息
            if (!virtual_names) {
                in_addr[a].names.nelts = 0;
            }
        }

        /*
         * if there's the binding to "*:port" then we need to bind()
         * to "*:port" only and ignore the other bindings
         */
        // 地址列表的最后一项是不是any,如果绑定的地址中有any,则绑定该地址即可,其他的地址不需要绑定了
        if (in_addr[a - 1].addr == INADDR_ANY) {
            a--;

        } else {
            a = 0;
        }

        in_addr = in_port[p].addrs.elts;
        while (a < in_port[p].addrs.nelts) {
            // push到cycle->listening中,并填充某些字段
            ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
                                                  in_port[p].port);
            if (ls == NULL) {
                return NGX_CONF_ERROR;
            }

            ls->backlog = -1;
#if 0
#if 0
            ls->nonblocking = 1;
#else
            ls->nonblocking = 0;
#endif
#endif
            ls->addr_ntop = 1;

            ls->handler = ngx_http_init_connection;
            // 地址对应的server配置
            cscf = in_addr[a].core_srv_conf;
            ls->pool_size = cscf->connection_pool_size;
            ls->post_accept_timeout = cscf->post_accept_timeout;

            clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
            ls->log = clcf->err_log;

#if (WIN32)
            iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
            if (iocpcf->acceptex_read) {
                ls->post_accept_buffer_size = cscf->client_header_buffer_size;
            }
#endif

            ls->ctx = ctx;
            // 有多个地址并且没有监听any地址
            if (in_port[p].addrs.nelts > 1) {

                in_addr = in_port[p].addrs.elts;
                // 没有绑定any的配置
                if (in_addr[in_port[p].addrs.nelts - 1].addr != INADDR_ANY) {

                    /*
                     * if this port has not the "*:port" binding then create
                     * the separate ngx_http_in_port_t for the all bindings
                     */
                    /*
                        分配一个新的端口和地址结构,形成一个端口对应一个地址的结构,
                        但是servername是共享的,比如端口1*地址数+端口2*地址数个结构体
                    */
                    ngx_test_null(inport,
                                  ngx_palloc(cf->pool,
                                             sizeof(ngx_http_in_port_t)),
                                  NGX_CONF_ERROR);
                    // 复制
                    inport->port = in_port[p].port;
                    inport->port_text = in_port[p].port_text;

                    /* init list of the addresses ... */

                    ngx_init_array(inport->addrs, cf->pool, 1,
                                   sizeof(ngx_http_in_addr_t),
                                   NGX_CONF_ERROR);

                    /* ... and set up it with the first address */
                    // 地址等于当前循环的地址
                    inport->addrs.nelts = 1;
                    inport->addrs.elts = in_port[p].addrs.elts;

                    ls->servers = inport;

                    /* prepare for the next cycle */
                    // 指向地址列表中下一个地址
                    in_port[p].addrs.elts = (char *) in_port[p].addrs.elts
                                                       + in_port[p].addrs.size;
                    in_port[p].addrs.nelts--;

                    in_addr = (ngx_http_in_addr_t *) in_port[p].addrs.elts;
                    a = 0;

                    continue;
                }
            }

            ls->servers = &in_port[p];
            a++;
        }
    }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 初始化listening结构体中ip地址和端口相关的字段
ngx_listening_t *ngx_listening_inet_stream_socket(ngx_conf_t *cf,
                                                 in_addr_t addr,
                                                 in_port_t port)
{
    size_t               len;
    ngx_listening_t     *ls;
    struct sockaddr_in  *addr_in;

    if (!(ls = ngx_array_push(&cf->cycle->listening))) {
        return NULL;
    }

    ngx_memzero(ls, sizeof(ngx_listening_t));

    if (!(addr_in = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)))) {
        return NULL;
    }

#if (HAVE_SIN_LEN)
    addr_in->sin_len = sizeof(struct sockaddr_in);
#endif
    addr_in->sin_family = AF_INET;
    addr_in->sin_addr.s_addr = addr;
    addr_in->sin_port = htons(port);

    if (!(ls->addr_text.data = ngx_palloc(cf->pool, INET_ADDRSTRLEN + 6))) {
        return NULL;
    }

    len = ngx_inet_ntop(AF_INET, &addr, ls->addr_text.data, INET_ADDRSTRLEN);
    ls->addr_text.len = ngx_snprintf((char *) ls->addr_text.data + len,
                                     6, ":%d", port);

    ls->fd = (ngx_socket_t) -1;
    ls->family = AF_INET;
    ls->type = SOCK_STREAM;
    ls->protocol = IPPROTO_IP;
#if (WIN32)
    ls->flags = WSA_FLAG_OVERLAPPED;
#endif
    ls->sockaddr = (struct sockaddr *) addr_in;
    ls->socklen = sizeof(struct sockaddr_in);
    ls->addr = offsetof(struct sockaddr_in, sin_addr);
    ls->addr_text_max_len = INET_ADDRSTRLEN;

    return ls;
}

端口、地址、servername处理完后的内存视图如下。

在这里插入图片描述

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
解读Nginx:深入剖析HTTP启动流程
1、遇到conf文件的http模块。http不是在Nginx的mian函数中启动,而是解析conf文件时遇到http才会去解析并启动。
Lion Long
2025/01/08
1240
解读Nginx:深入剖析HTTP启动流程
十一长假精荐!--nginx http请求PHASE
当客户端的一个request到达服务器的时候,可以想象一下,一个服务器应该怎么处理这个请求呢?nginx又是怎么处理请求的呢?
后端技术探索
2018/08/09
8070
nginx0.1.0之event模块初始化源码分析(2)
本文分析nginx的event在初始化时的第一个步骤,就是执行每个类型为NGX_EVENT_MODULE的模块的create_conf函数。该版本一共有8个这样的模块。下面我们看一下各个模块create_conf函数的代码。 这是ngx_event_core_module模块的create_conf函数。
theanarkh
2019/03/06
4290
nginx0.1.0之http模块初始化源码分析(5)
继续http模块的分析,这次分析的是init_module钩子函数的执行。http模块各子模块的init_module钩子主要做的事情是两个,一个就是注册phases回调,一个就是挂载模块到filter链表。首先看一下注册phases回调的模块 access模块
theanarkh
2019/03/06
4110
nginx0.1.0之http模块初始化源码分析(1)
http模块的初始化类似event模块,初始化的起点在解析到http指令的时候。对应的处理函数是ngx_http_block,因为该函数比较长,所以我们分段解析。第一部分先解析http模块的pre_conf、create_main_conf函数的实现。
theanarkh
2019/03/06
5980
nginx0.1.0之event模块初始化源码分析(4)
event的配置解析相关的代码已经分析完毕。下面分析一下另一个流程中event模块的实现。即在nginx创建进程,并且开始执行进程里的代码的时候。入口函数是ngx_worker_process_cycle。
theanarkh
2019/03/06
3900
【Nginx 源码学习】平滑重启,源码追踪
重启意味着新旧接替,在交接任务的过程中势必会存在新旧server并存的情形,因此,最主要的问题在于如何保证新旧server可以并存,如果重启前后的server端口一致,如何保证两者可以监听同一端口。
看、未来
2022/05/06
8210
【Nginx 源码学习】平滑重启,源码追踪
nginx0.1.0之http模块初始化源码分析(3)
我们继续分析ngx_http_block函数的代码,指令解析完,继续执行各子模块的钩子函数。
theanarkh
2019/03/06
5290
深入理解nginx realip模块
  nginx 的 Real IP 模块用于解决代理服务器转发请求到nginx上时可能出现的 IP 地址问题。因为当 PROXY收到客户端的请求时,它会通过自己的IP与nginx服务器连接并转发请求。这会导致在nginx应用程序中记录的 IP 地址是代理服务器的地址,而不是实际客户端的地址。
码农心语
2024/04/10
1.3K0
深入理解nginx realip模块
nginx日志模块源码分析
请求在处理结束时,会按请求路径的配置上下文记访问日志。 如果在请求处理期间产生了内部跳转(参考另一篇nginx跳转讲述), 请求结束时的路径可能不同于原始的请求路径。
stan1ey
2021/06/07
1.6K0
nginx日志模块源码分析
Nginx(六):配置解析之location解析
nginx成为非常流行的代理服务软件,最根本的原因也许是在于其强悍性能。但还有一些必要的条件,比如功能的完整,配置的易用,能够解决各种各样的实际需求问题,这些是一个好的软件的必备特性。
烂猪皮
2021/01/28
2K0
Nginx(六):配置解析之location解析
nginx0.1.0之http模块初始化源码分析(2)
本文讲解http各个模块create_srv_conf和create_loc_conf钩子,还有指令的解析。 各模块的create_srv_conf和create_loc_conf函数逻辑都类似,不一一列举,执行完后内存视图是。
theanarkh
2019/03/06
3870
nginx0.1.0之event模块初始化源码分析(1)
nginx模块初始化的流程在下面的代码中,核心模块的初始化,各核心模块首先在create_conf中创建保存配置的数据结构,然后在ngx_conf_parse中,通过解析命令,执行对应的命令处理函数,完成赋值和各核心模块的子模块初始化。最后,如果在ngx_conf_parse时,没有设置值,则执行init_conf函数进行默认初始化。
theanarkh
2019/03/06
4910
nginx0.1.0之event模块初始化源码分析(3)
前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历配置文件的内容,然后读取命令和参数。最后匹配nginx所有模块的配置,找到处理该指令的函数。我们首先看一下event模块中ngx_event_core_module模块的指令配置。
theanarkh
2019/03/06
5680
nginx0.1.0 access_handler模块源码分析
access模块主要是对连接到nginx中的客户端进行权限管理,nginx会根据access模块注册的命令,在解析命令的时候,遇到对应的命令的时候,会调用access注册的处理函数,access模块的处理函数会解析并记录这些配置,然后在每个连接到来时,判断ip是否在封禁的池子了。nginx只判断deny的规则,即同一个ip命中多条规则的时候,只要有一条是deny则结果就是deny。即deny优先。
theanarkh
2019/04/24
4070
nginx源代码分析–模块分类
比方。对一个普通的訪问本地静态文件的请求处理,从 Nginx 收到请求并開始处理。到处 理结果的响应包体发送到网络上结束,整个过程的两个步骤 – 请求处理和响应处理 – 分别 由 handler 和 filter 处理完毕。
全栈程序员站长
2022/07/07
7230
nginx处理http请求十一个阶段流程源码分析
使用 ngx_http_phase_t 结构存储每个阶段可用的处理函数(handler)。
stan1ey
2021/06/07
1.4K0
nginx处理http请求十一个阶段流程源码分析
nginx1.17.9源码分析之线程池
我们发现事件驱动的软件都得配一个线程池。libuv和nginx都是。因为事件驱动的软件是单线程。但是有些事情总会引起线程阻塞。所以这个事情就不能放到主线程里做。这就是为什么事件驱动都要配一个线程池。把任务交给线程池中的线程。主线程继续执行。任务完成后通知主线程或者执行回调就行。 我们先看一下nginx线程池的架构。然后开始分析。
theanarkh
2020/03/31
5940
nginx1.17.9源码分析之线程池
深入理解nginx的https sni机制
  SNI(Server Name Indication)是一种TLS(Transport Layer Security)协议的扩展,用于在建立加密连接时指定服务器的主机名。在使用单个IP地址和端口提供多个域名的服务时,SNI是非常有用的。  当客户端发起TLS握手时,它会发送一个包含所请求主机名的扩展,这样服务器就可以根据这个主机名选择合适的证书来完成握手。这使得服务器能够在同一IP地址和端口上为多个域名提供加密连接,而不需要为每个域名分配一个独立的IP地址。  对于HTTPS网站来说,SNI是至关重要的,因为它允许服务器在同一IP地址上为多个域名提供加密连接,不需要为每个域名单独部署一台服务器,从而降低了运维成本并提高了灵活性。  在使用SNI时,服务器端必须能够根据客户端发送的SNI信息来选择正确的证书进行握手。通常,服务器端配置会包含多个虚拟主机的证书信息,以便根据收到的SNI信息选择正确的证书来完成握手。  总的来说,SNI允许客户端在TLS握手期间指定所请求的主机名,从而使服务器能够根据主机名选择正确的证书,实现一个IP地址上多个域名的加密连接。
码农心语
2024/04/09
3.1K1
深入理解nginx的https sni机制
nginx proxy_bind支持多ip绑定
proxy_bind隶属于proxy_module,为向后端建立连接时的local ip,在nginx源码中只支持bind一个ip进行回源,若想使用多个ip进行回源时,可以修改源码支持bind ip数组。在实际应用中我就是这样做的。bind ip数据轮询选择ip进行回源与upstream建立连接,以解决单ip回源连接数限制问题。下面proxy_bind部分就是针对proxy_bind进行优化后的代码,支持bind多ip。
stan1ey
2021/06/07
4.4K0
nginx proxy_bind支持多ip绑定
相关推荐
解读Nginx:深入剖析HTTP启动流程
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验