nginx 代码分析listen 和request请求的流程

本文深入探讨Nginx的listen机制,包括如何配置不同端口和服务名称,以及解析其内部实现原理,从ngx_http_add_listen函数到ngx_http_init_connection等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

listen 针对于server的端口进行监听,server的配置可以比较复杂如:

server {
        listen       192.168.8.81:80;
        server_name  vae.test1.com *.test1.com;<!--hosts文件配置的地址-->
        index index.html;
        location /test1 {
           root /usr/local/nginx/html/;
        }
    }
    server {
        listen       127.0.0.1:80;
        server_name  vae.test1.com *.test1.com;<!--hosts文件配置的地址-->
        index index.html;
        location /test1 {
           root /usr/local/nginx/html/;
        }
    }

    server {
        listen       80;
        server_name  vae.test1.com;<!--hosts文件配置的地址-->
        index index.html;
        location /test1 {
           root /usr/local/nginx/html/;
        }
    }
server {
        listen       80;
        server_name  vae.test2.com;
        index index.html;
        location /test2 {
            root /usr/local/nginx/html/;
        }
    }
server {
        listen       8081;
        server_name  vae.test3.com;
        index index.html;
        location /test3 {
            root /usr/local/nginx/html/;
        }
    }

//完成之后需要重新启动下nginx命令:nginx -s reload,每次修改nginx.conf文件都需要重启

host配置:

#   127.0.0.1       localhost
#   ::1             localhost
192.168.8.81 vae.test1.com
192.168.8.81 vae.test2.com
192.168.8.81 vae.test3.com

ngx_http_add_listen 函数将解析http中的server的地址和端口,以port为粒度,添加addr(即1.1.1.1:80、2.2.2.2:80和80,会放在相同的cmcf->port[i]中)。
时序图和数据结构关系图:
这里写图片描述

那么在cmcf中的存储例子:

这里写图片描述

然后,就是在ngx_http_optimize_servers 中优化后加入cf->cycle->listening中(待完善)

接着在ngx_init_cycle –> ngx_open_listening_sockets中打开和监听所有的端口创建socket

在启动过程中的 ngx_worker_process_init中调用ngx_modules[event]->init_process(cycle) 初始化connections 并设置事件的回调 ,以ngx_epoll_moudle为例:

  • ngx_event_process_init中 listen流程
/* 初始化用来管理所有定时器的红黑树,用于事件的处理轮询 */
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
    return NGX_ERROR;
 }
 ....
 //分配和初始化cycle->connections,设置每个c的读写事件event
cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
c = cycle->connections;
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);
do {
     i--;
     c[i].data = next;
     c[i].read = &cycle->read_events[i]; //关联c[i]和rev wev
     c[i].write = &cycle->write_events[i];
     c[i].fd = (ngx_socket_t) -1;
     next = &c[i];
 } while (i);
 //针对监听的每个端口,分配connecting,并设置回调函数,加入到epoll池中
 //c = ngx_cycle->free_connections;
 ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {
        c = ngx_get_connection(ls[i].fd, cycle->log);
        ....
        rev->handler = ngx_event_accept;
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }
  • ngx_event_accept请求的处理流程
    -这里涉及到ngx_use_accept_mutex的问题,用于解决多进程之间对请求的均衡处理(后续理解)
//epoll 中接收到请求后的回调,建立socket连接
void ngx_event_accept(ngx_event_t *ev)
{
    lc = ev->data;
    ls = lc->listening;
    s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
    if (ngx_use_accept_mutex) {//进程竞争锁
        if (ngx_accept_mutex_held) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            ngx_accept_mutex_held = 0;
            }
            ngx_accept_disabled = 1;
    } else {
        ngx_add_timer(ev, ecf->accept_mutex_delay);
    }
    c = ngx_get_connection(s, ev->log);
}
//加入到epoll中等待read事件
 if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
        if (ngx_add_conn(c) == NGX_ERROR) {
            ngx_close_accepted_connection(c);
             return;
        }
 }
 //在ngx_http_add_listening时ls->handler = ngx_http_init_connection;设置回调
 ls->handler(c);//初始化这个对话connection
void ngx_http_init_connection(ngx_connection_t *c)
{//初始化一个request的connection
    //设置这个listen的addr_conf和conf_ctx,当数据可读时候用于解析
    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
    if (hc == NULL) {
        ngx_http_close_connection(c);
        return;
    }
    c->data = hc;
    ....
    rev = c->read;//初始化read的epoll毁掉函数
    rev->handler = ngx_http_wait_request_handler;
    c->write->handler = ngx_http_empty_handler;
    //设置这个链接的超时timer
    ngx_add_timer(rev, c->listening->post_accept_timeout);
}

具体的时序图:

这里写图片描述

  • ngx_http_wait_request_handler

当epoll中此connection可读,调用ngx_http_wait_request_handler(ngx_event_t *rev)
关于rev和connection中的关键数据data的关系

//request 中的main_conf/srv_conf/loc_conf
ngx_http_wait_request_handler(ngx_event_t *rev)
{
    n = c->recv(c, b->last, size);
    ....    
    c->data = ngx_http_create_request(c); //这里创建request
    {
        r->main_conf = hc->conf_ctx->main_conf;
        r->srv_conf = hc->conf_ctx->srv_conf;
        r->loc_conf = hc->conf_ctx->loc_conf;
    }

}
  1. ngx_get_connection中 rev->data = c;

  2. ngx_http_init_connection(*c) -> ngx_http_wait_request_handler(rev) -> c->data = ngx_http_create_request(rev)

这里写图片描述

//设置request 的connect到recv中:
ngx_http_process_request_line(ngx_event_t *rev)
{
    ngx_connection_t    *c;
    ngx_http_request_t  *r;

    c = rev->data; //ngx_get_connection中 rev->data = c;
    r = c->data;  
    //这里的data设置:ngx_http_init_connection(*c) -> ngx_http_wait_request_handler(rev) -> c->data = ngx_http_create_request(rev)
}

最后当请求request过来的时候就能够从rev中获取到request并且里面有相应的ngx_http_core_loc_conf_t

ngx_http_core_find_location 回顾上一篇文章中,根据url和ngx_http_core_loc_conf_t 的三叉排序树或者正则数组 来获取正确模块的loc_conf。

最后就可以调用模块的content_handler函数传入req->locf 完成整请求(待完善)

后续会针对初始化event 和 请求的流程写详细一点,这里只是从listen出发后续展开。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值