nginx之worker启动分析
来源:互联网 发布:中国 改革开放 知乎 编辑:程序博客网 时间:2024/06/05 18:25
在多进程模式下,启动worker工作进程的逻辑从ngx_start_worker_processes开始,它包含3个参数,第一个cycle为全局的一个核心结构体,第二个参数n表示需要创建的worker进程数,第三个参数type为工作进程类型,这里为NGX_PROCESS_RESPAWN,表示当worker进程异常中止时master进程将会重新启动一个新的worker。
#file:ngx_process_cycle.c
static voidngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type){ ngx_int_t i; ngx_channel_t ch; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); ngx_memzero(&ch, sizeof(ngx_channel_t)); ch.command = NGX_CMD_OPEN_CHANNEL; //n进程数 for (i = 0; i < n; i++) { ngx_spawn_process(cycle, ngx_worker_process_cycle, (void *) (intptr_t) i, "worker process", type); ch.pid = ngx_processes[ngx_process_slot].pid;//新创建的进程id ch.slot = ngx_process_slot; //在ngx_spawn_process中被赋值 ch.fd = ngx_processes[ngx_process_slot].channel[0]; ngx_pass_open_channel(cycle, &ch); }}
在讨论master与worker进程间的通信时会要涉及到一个重要的数据结构ngx_channel_t,也叫频道,它是使用本地套接字实现。利用下面的socketpair方法,就可以创建父子进程间使用的套接字。
int socketpair(int d, int type, int protocol, int sv[2]);
当socketpair执行成功时,sv[2]这两个套接字有下列关系:向sv[0套接字写入数据,将可以从sv[1]套接字中读取数据;同样,向sv[1]套接字写入数据,也可以从sv[0]中读取到写入的数据。在父子进程间应用时,在父进程中调用socketpair创建这样一组套接字,接着调用fork创建出子进程。然后在父进程中关闭sv[1]套接字,在子进程中关闭sv[0]套接字,这样父进程可以用sv[0]和子进程的sv[1]自由地进行双向通信。for loop中会调用ngx_spawn_process,该函数主要就是调用socketpair创建流式套接字组,然后调用fork创建出子进程,并在子进程中运行进程函数ngx_worker_process_cycle。每创建一个新的worker,都需要向其它worker同步新建worker的信息,这个由ngx_pass_open_channel来完成,其第2个参数为指向ngx_channel_t的指针,ngx_channel_t结构如下:
typedef struct {
ngx_uint_t command;//传递的TCP消息中的命令
ngx_pid_t pid;//进程id
ngx_int_t; //ngx_processes进程数组中的序号
ngx_fd_t fd;//通信的套接字句柄
}ngx_channel_t;
这个简单的结构便用来同步master进程与worker进程间的状态。每创建一个新的进程,频道中pid成员将赋值为新进程的id,slot赋值为新进程在ngx_processes进程数组中的序号,fd为socketpair创建的进程组中的sv[0],这个进程数组的信息被保存在ngx_process_t结构体的channel成员中,ngx_process_t是描述进程的结构。
static voidngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch){ ngx_int_t i; for (i = 0; i < ngx_last_process; i++) { if (i == ngx_process_slot || ngx_processes[i].pid == -1 || ngx_processes[i].channel[0] == -1) { continue; } ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d", ch->slot, ch->pid, ch->fd, i, ngx_processes[i].pid, ngx_processes[i].channel[0]); ngx_write_channel(ngx_processes[i].channel[0], ch, sizeof(ngx_channel_t), cycle->log); }}
在ngx_pass_open_channel函数中,for循环每次获取一个非新建的有效worker进程的索引,并通过ngx_write_channel函数向该worker传递新建进程信息。其中ngx_write_channel的第一个参数为与该worker进行通信的流式套接字数组中的channel[0],worker进程在channel1]上会收到传递的信息。这里有个疑问是,worker进程如何得知channel[1]上有消息到达,这是因为在worker的进程函数中,已经将channel[1]上的读事件添加到了事件驱动中,并绑定读事件回调函数为ngx_channel_handler。
static voidngx_channel_handler(ngx_event_t *ev){ ngx_int_t n; ngx_channel_t ch; ngx_connection_t *c; if (ev->timedout) { ev->timedout = 0; return; } c = ev->data; ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler"); for ( ;; ) { n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);//从fd中读信息 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n); if (n == NGX_ERROR) { if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { ngx_del_conn(c, 0); } ngx_close_connection(c); return; } if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) { if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { return; } } if (n == NGX_AGAIN) { return;//返回继续等待事件 } ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel command: %d", ch.command); 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_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0, "get channel s:%i pid:%P fd:%d", ch.slot, ch.pid, ch.fd); ngx_processes[ch.slot].pid = ch.pid; ngx_processes[ch.slot].channel[0] = ch.fd; break; case NGX_CMD_CLOSE_CHANNEL: ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0, "close channel s:%i pid:%P our:%P fd:%d", ch.slot, ch.pid, ngx_processes[ch.slot].pid, ngx_processes[ch.slot].channel[0]); 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; } }}
- nginx之worker启动分析
- nginx的worker-master启动之worker进程
- nginx的worker-master启动之master进程
- Spark集群启动之Master、Worker启动流程源码分析
- Nginx源码分析-进程管理之worker进程
- Nginx源码分析-进程管理之worker进程
- Nginx源码分析---worker进程
- Spark源码分析之worker节点启动driver和executor
- Spark源码分析之Worker启动通信机制
- Spark分析之Worker
- nginx源代码分析 - 启动(四) 创建后台进程和worker进程
- nginx源代码分析 - 启动(五) 调试后台进程和worker进程
- nginx源代码分析 - 启动(六) 关于worker进程绑定CPU的细节
- 3supervisor启动worker源码分析-worker.clj
- Spark源码分析之Worker
- Spark源码分析之Worker
- Spark源码分析之Worker
- worker启动executor源码分析-executor.clj
- 找出旋转数组中的最小数字
- 集合操作retainAll和removeAll
- 复习
- cuda编程基础概念语法
- 杀死oracle进程
- nginx之worker启动分析
- Java反射
- 27. Remove Element
- SQLite数据库锁问题
- scala学习笔记(1)
- C# Winform Datagridview 排序 多线程操作数据 遇到的坑
- 查找错误千万要拓展思维!
- eclipse空间目录管理——查看与删除
- YOLO