Nginx多进程并发连接处理模型

Linux大全评论639 views阅读模式

你知道的,并发连接是任何服务端程序都逃不掉的重要的性能指标。如何处理大量并发的连接无疑是服务端程序设计时所要考虑的第一个问题。这里简单的看看Nginx是如何处理并发的http连接的。

总体结构如下图所示:

对于服务端来讲,处理并发连接无疑要达到的效果是:高并发,快响应。Nginx架构采用的是Master-Worker的多进程协作模式。所以如何让每个worker进程都平均的处理连接也是一个要考虑的问题。

就上图,listen套接字是Master进程初始化的时候创建的,然后fork子进程的时候自然的继承给子进程的。上代码。

在src/core/nginx.c的main函数里依次有如下两行调用:

1 2 3 cycle = ngx_init_cycle(&init_cycle);    ngx_master_process_cycle(cycle);

在nginx代码中,一个cycle代表一个进程,所有进程相关变量(包括连接)都在这个结构体里。main函数里先调用ngx_init_cycle来初始化了一个主进程实例,80端口的监听套接字也是在这个函数里创建的:

1 2 3 if (ngx_open_listening_sockets(cycle) != NGX_OK) {          goto failed; }

在ngx_open_listening_sockets函数的代码中可以看到bind、listen等套接字函数的调用。最终创建完的监听套接字就在cycle结构体的listening域里。相关代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ls = cycle->listening.elts;    s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0);    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,                             ( const void *) &reuseaddr, sizeof ( int ))                  == -1)              {...    if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {...    if (listen(s, ls[i].backlog) == -1) {...    ls[i].listen = 1;    ls[i].fd = s;

你懂的。

main函数里面调用的ngx_master_process_cycle就是创建worker进程的地方了。

在ngx_master_process_cycle函数里调用了ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN);函数,在ngx_start_worker_processes函数里我们有能看到

1 2 3 4 5 6 7 8 9 10 11 12 13 for (i = 0; i < n; i++) {    cpu_affinity = ngx_get_cpu_affinity(i);    ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL, "worker process" , type);    ch.pid = ngx_processes[ngx_process_slot].pid; ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0];    ngx_pass_open_channel(cycle, &ch); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 if (ngx_use_accept_mutex) { if (ngx_accept_disabled > 0) { ngx_accept_disabled--;    //此处ngx_accept_disabled其实是进程的最大连接数(配置文件中指定)的1/8减去剩余连接数的差    //在src/event/nginx_event_accept.c中计算:ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;    //当剩余连接数小于最大连接数的1/8的时候为正,表示连接有点多了,于是放弃一次争锁定机会    } else { if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {    //这里ngx_trylock_accept_mutex函数就是争锁定函数,成功争得了锁则将全局变量ngx_accept_mutex_held置为1,否则置0 return ; }    if (ngx_accept_mutex_held) {    flags |= NGX_POST_EVENTS;    //占用了accept锁的进程在处理事件的时候是先将事件放入队列,后续慢慢处理,以便尽快走到下面释放锁。    } else {    //没争得锁的进程不需要分两步处理事件,但是把处理事件的timer更新为ngx_accept_mutex_delay if (timer == NGX_TIMER_INFINITE    || timer > ngx_accept_mutex_delay) {    timer = ngx_accept_mutex_delay; } } } }    delta = ngx_current_msec;    //下面这个函数就是处理事件的函数(包括新连接建立事件),网络IO事件等等    ( void ) ngx_process_events(cycle, timer, flags);    delta = ngx_current_msec - delta;    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle-> log , 0, "timer delta: %M" , delta);    if (ngx_posted_accept_events) {    //这里处理队列中的accept事件,这里面将会调用ngx_event_accept来建立新连接 ngx_event_process_posted(cycle, &ngx_posted_accept_events); }    if (ngx_accept_mutex_held) { ngx_shmtx_unlock(&ngx_accept_mutex);    //好了,释放accept锁 }

企鹅博客
  • 本文由 发表于 2020年9月27日 08:38:46
  • 转载请务必保留本文链接:https://www.qieseo.com/142570.html

发表评论