Nginx源码分析(4)

前面分析了Nginx的基本数据结构,现在看一下功能模块。

Nginx将各功能模块组成一条链,当有请求到达时,请求依次进过这条链上的部分或全部模块进行处理。


线程模型

Nginx使用一个多进程模型来提供服务,一个master进程和多个worker进程。类似的原理可以参见分布式进程。框架如下:

图片

master分管多个work进程。每个worker执行死循环接收来自客户端的请求。死循环由ngx_worker_process_cycle实现。一个请求的简单处理流程如下:

  • 操作系统提供的机制(例如epoll, kqueue等)产生相关的事件
  • 接收和处理这些事件,如是接受到数据,则产生更高层的request对象
  • 处理request的header和body
  • 产生响应,并发送回客户端
  • 完成request的处理
  • 重新初始化定时器及其他事件

show the code

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
    ngx_int_t worker = (intptr_t) data;

    ngx_uint_t         i;
    ngx_connection_t  *c;

    ngx_process = NGX_PROCESS_WORKER;
    ngx_worker = worker;
    //设定n个worker进程
    ngx_worker_process_init(cycle, worker);
    ngx_setproctitle("worker process");

    for ( ;; ) {
        //如果进程退出,关闭所有连接
        if (ngx_exiting) {
            c = cycle->connections;
            for (i = 0; i < cycle->connection_n; i++) {
                if (c[i].fd != -1 && c[i].idle) {
                    c[i].close = 1;
                    c[i].read->handler(c[i].read);
                }
            }
            //定时器归0
            ngx_event_cancel_timers();
            //判断timer的红黑树是否为空
            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel) {
                ngx_worker_process_exit(cycle);
            }
        }

        ngx_process_events_and_timers(cycle);
        //处理中断信号
        if (ngx_terminate) {
            ngx_worker_process_exit(cycle);
        }
        //处理退出信号
        if (ngx_quit) {
            ngx_quit = 0;
            ngx_setproctitle("worker process is shutting down");
            if (!ngx_exiting) {
                ngx_close_listening_sockets(cycle);
                ngx_exiting = 1;
            }
        }
        //处理重启log
        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_reopen_files(cycle, -1);
        }
    }
}

其中几个关键函数是

ngx_worker_process_init 处理子进程初始化

ngx_process_events_and_timers(cycle)

ngx_worker_process_exit(cycle) 子进程退出

下面来看一下子进程的初始化操作:

  • 全局性的设置,根据全局的配置信息设置执行环境、优先级、限制、setgid、setuid、信号初始化等
  • 调用所有模块的钩子init_process
for (i = 0; ngx_modules[i]; i++) {
    if (ngx_modules[i]->init_process) {
        if (ngx_modules[i]->init_process(cycle) == NGX_ERROR{            exit(2);                
        }
    }
}

里面关键的函数是init_process

  • 关闭不使用的socket,关闭当前worker的channel[0]句柄和其他worker的channel[1]句柄,当前worker会使用其他worker的channel[0]句柄发送消息,使用当前worker的channel[1]句柄监听可读事件
for (n = 0; n < ngx_last_process; n++) {

    if (ngx_processes[n].pid == -1) {
        continue;
    }

    //跳过当前worker
    if (n == ngx_process_slot) {
        continue;
    }

    if (ngx_processes[n].channel[1] == -1) {
        continue;
    }
    //关闭其它workers channel[1]
    if (close(ngx_processes[n].channel[1]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() channel failed");
    }
}

//关闭当前worker channel[0]
if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                  "close() channel failed");
}
  • 在当前worker的channel[1]句柄监听可读事件
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,ngx_channel_handler) == NGX_ERROR)
{
    exit(2);
}

ngx_add_channel_event把句柄ngx_channel(当前worker的channel[1])上建立的连接的可读事件加入事件监控队列,事件处理函数为ngx_channel_hanlder。当有可读事件的时候,ngx_channel_handler负责处理消息,具体代码可以查看src/os/unix/ngx_process_cycle.c,过程如下:

static void
ngx_channel_handler(ngx_event_t *ev)
{
//...
for ( ;; ) {
    //从channe[1]读取消息
    n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log); 
    //处理消息命令
    switch (ch.command) {
        case NGX_CMD_QUIT:
            ngx_quit = 1;
            break;
        case NGX_CMD_TERMINATE:
            ngx_terminate = 1;
            break;
        case NGX_CMD_REOPEN:
            ngx_reopen = 1;
            break;
        case NGX_CMD_OPEN_CHANNEL:
            ngx_processes[ch.slot].pid = ch.pid;
            ngx_processes[ch.slot].channel[0] = ch.fd;
            break;
        case NGX_CMD_CLOSE_CHANNEL:
            if (close(ngx_processes[ch.slot].channel[0]) == -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                              "close() channel failed");
            }
            ngx_processes[ch.slot].channel[0] = -1;
            break;
        }
    }
}

##参考

http://blog.csdn.net/lengzijian/article/details/7591025



Previous     Next
tuzhi /
Published under (CC) BY-NC-SA in categories 服务器  tagged with