IT技术博客大学习 共学习 共进步

Nginx进程管理之worker进程

淘宝数据平台与产品部官方博客 tbdata.org 2010-12-02 22:31:28 浏览 4,765 次

上一篇博文分析了master进程,本文着手分析一下worker进程的情况。首先找到worker进程的入口地方――ngx_worker_process_cycle。这个函数不光是worker进程的入口函数,同时也是worker进程循环工作的主体函数,看函数名含有一个cycle嘛。进入这个cycle函数,第一件事就是调用ngx_worker_process_init(cycle, 1);对worker进程进行初始化操作。先看看这个worker进程的初始化过程。

    ngx_process = NGX_PROCESS_WORKER;
    if (ngx_set_environment(cycle, NULL) == NULL) {
        /* fatal */
        exit(2);
    }

进入初始化就将全局变量ngx_process设置为worker进程的标志,由于这个变量是从master进程复制过来的,所以没设置前就是master进程的标志。然后设置相应的环境变量。接下去就是设置了一些列的资源限制,id等玩意,这里就忽略代码了。

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->init_process) {
            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
                /* fatal */
                exit(2);
            }
        }
    }

此处循环调用每个模块的init_process,完成每个模块自定义的进程初始化操作,一般在模块定义的时候设置这个回调指针的值,即注册一个函数给它。做模块开发的时候,貌似使用得挺少的,遇到的时候好好关注下。

/*
    此处循环用于关闭其他worker进程的无用channel资源
    */
    for (n = 0; n  < ngx_last_process; n++) {

	/* ngx_processes数组中n位置的进程不存在。*/
        if (ngx_processes[n].pid == -1) {
            continue;
        }
	/*
	全局变量ngx_process_slot的值是创建worker进程的时候,从
	master进程复制过来的,所以此处ngx_process_slot就指本worker
	进程在ngx_process_slot数组中的索引位置。此处不处理本worker
	进程,所以跳过。
	*/
        if (n == ngx_process_slot) {
            continue;
        }
	/*
	channel不存在,继续跳过。
	*/
        if (ngx_processes[n].channel[1] == -1) {
            continue;
        }
	/*
	ngx_processes数组中存储的是每个worker进程的资源,是master进程负责创建的。
	因此创建一个worker进程的时候,就一同将这些资源复制过来了,所以此处就关闭
	无用的channel――其他worker进程的读端文件描述符,保留写端文件描述符做
	worker间的通信之用。
	*/
        if (close(ngx_processes[n].channel[1]) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "close() channel failed");
        }
    }
	/*
	关闭本worker进程channel的写端文件描述符,因为每个worker进程只从自己的channel
	上读,而不会写。写自己channel的是master和其他worker进程。这也是上面为什么要
	关闭其他worker进程channel的读端。
	*/
    if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() channel failed");
    }

针对这段代码采用直接注释代码的方式进行分析,感觉挺自然的,还不错,以后碰到比较长的代码段都采用这种方式进行了。

    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
                              ngx_channel_handler)
        == NGX_ERROR)
    {
        /* fatal */
        exit(2);
    }

ngx_channel就是worker进程channel的读端,这里调用ngx_add_channel_event将channel放入Nginx关心的集合中,同时关注起这个channel上的读事件,也即这个channel上有数据到来后,就立马采取读channel操作。此处的添加一个channel的读事件是worker进程初始化的关键之处。到此,初始化过程就结束了,回到worker循环主体看看吧。

  	for ( ;; ) {
	/*
	ngx_exiting是在worker进程收到SIGQUIT信号后设置的,稍后就能看到庐山真面目了。
	*/
        if (ngx_exiting) {
            c = cycle->connections;
		/*
		worker进程退出前,先得处理完每个connection上已经发生的事件。
		*/
            for (i = 0; i connection_n; i++) {
                /* THREAD: lock */
                if (c[i].fd != -1 && c[i].idle) {
                    c[i].close = 1;
                    c[i].read->handler(c[i].read);
                }
            }

		/*
		处理完所有事件后,调用ngx_worker_process_exit函数,worker进程退出。
		*/
            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
            {
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
                ngx_worker_process_exit(cycle);
            }
        }
        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
	/*
	这里是worker进程处理事件的核心开始。也即是,worker进程从里开始做一些特定的事情了,
	我们完全可以修改此处的代码,让Nginx为我们做一些其他的事情,呵呵。
	*/
        ngx_process_events_and_timers(cycle);
	/*
	worker进程收到了SIGINT信号,退出。
	*/
        if (ngx_terminate) {
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
            ngx_worker_process_exit(cycle);
        }

	/*
	worker进程收到了SIGQUIT信号,如果此时worker进程不是出于exiting状态,
	就将设置ngx_exiting为1,让其进入exiting状态;同时关闭监听套接口。
	*/
        if (ngx_quit) {
            ngx_quit = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                          "gracefully shutting down");
            ngx_setproctitle("worker process is shutting down");
            if (!ngx_exiting) {
                ngx_close_listening_sockets(cycle);
                ngx_exiting = 1;
            }
        }
	/*
	worker进程收到了SIGUSR1信号
	*/
        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, -1);
        }
    }

通过上述分析发现worker进程的cycle比master简单不少啊,到此,worker进程的大体框架就差不多了。下一篇计划分析master和worker间的channel通信。

建议继续学习

  1. 配置Nginx+uwsgi更方便地部署python应用 (阅读 106,824)
  2. 搜狐闪电邮箱的 Nginx/Postfix 使用模式 (阅读 33,762)
  3. 解析nginx负载均衡 (阅读 16,423)
  4. Oracle MTS模式下 进程地址与会话信息 (阅读 14,187)
  5. Linux内存点滴 用户进程内存空间 (阅读 12,946)
  6. Nginx模块开发入门 (阅读 11,041)
  7. 检查nginx配置,重载配置以及重启的方法 (阅读 10,683)
  8. Cacti 添加 Nginx 监控 (阅读 10,362)
  9. Nginx+FastCgi+Php 的工作机制 (阅读 10,084)
  10. 奇怪的 Nginx 的 upstream timed out 引起响应 502 (阅读 9,823)