zoukankan      html  css  js  c++  java
  • Nginx 事件基本处理流程分析

    说明:本文章重点关注事件处理模型。有兴趣的同学可以去http://tengine.taobao.org/book/查找更多资料。Tengine应该是淘宝基于Nginx自己做的修改。这个地址的文档还在不断的完善更新中,内容算是比较全面的。

    123

     程序流程图:

    说明:

    一、进程生成顺序

    1.main(src/core/nginx.c)函数启动ngx_master_process_cycle,启动主服务进程。

    2.ngx_master_process_cycle(src/os/unix/ngx_process_cycle.c)里面调用ngx_start_worker_processes.

    3.ngx_start_worker_processes(src/os/unix/ngx_process_cycle.c)里面循环执行流程图生成特定数量的进程池。

    4.ngx_spawn_process(src/os/unix/ngx_process.c)根据指定的respawn选项fork子进程,相关子进程信息保存在全部ngx_processes数组(ngx_process_t    ngx_processes[NGX_MAX_PROCESSES])中,子进程的运行过程通过参数proc指定,这里是ngx_worker_process_cycle(src/os/unix/ngx_process_cycle.c)。在fork之前先通过socketpair生成master process和worker process间进行通信的channel[2]。

    master_process进程作为Nginx的服务主进程,管理其他子进程的生存周期,包括cache_manager_processes子进程,全部worker_processes,信号处理,timer等。

    二、timer定时器超时处理机制

    上图中的左侧红色1,2,3步构成了timer和select/poll/epoll_wait等等待函数配合使用的基本流程,libevent里面的timer处理机制也是一样的,即:

    1.从timer树(一般使用红黑树)中取出最小的timer;

    2.传入epoll_wait等I/O复用函数;

    3.处理堆中的timer超时事件。

    4.处理正常的连接事件。

    流程图:

    补充:

    由于需要对大量timer进行实时增删和检索,所以需要效率比较高的结构,红黑树是理想选择。

    概念上说管理timer的树应该使用最小堆,每次只需要从树根取出最小的timer。但是堆得问题是插入和删除时都可能需要从根到叶节点的log(n)次交换,代价较大。

    而红黑树的好处是插入和删除时最多需要不超过3次旋转操作,虽然总的复杂度都是O(logN),但基本都是颜色变换和key比较等简单操作,不涉及节点交换(值交换)和旋转(指针交换)。所以,从统计性能(可理解为cpu实际执行的指令数)来说,红黑树优于堆和AVL树。

    三、worker process I/O处理流程

    每个worker process的运行过程ngx_worker_process_cycle如下:

     1 static void
     2 ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
     3 {
     4     ngx_int_t worker = (intptr_t) data;
     5 
     6     ngx_process = NGX_PROCESS_WORKER;
     7     ngx_worker = worker;
     8 
     9     ngx_worker_process_init(cycle, worker);
    10 
    11     ngx_setproctitle("worker process");
    12 
    13     for ( ;; ) {
    14 
    15         if (ngx_exiting) {
    16             ngx_event_cancel_timers();
    17 
    18             if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
    19             {
    20                 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
    21 
    22                 ngx_worker_process_exit(cycle);
    23             }
    24         }
    25 
    26         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
    27 
    28         ngx_process_events_and_timers(cycle);
    29 
    30         if (ngx_terminate) {
    31             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
    32 
    33             ngx_worker_process_exit(cycle);
    34         }
    35 
    36         if (ngx_quit) {
    37             ngx_quit = 0;
    38             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
    39                           "gracefully shutting down");
    40             ngx_setproctitle("worker process is shutting down");
    41 
    42             if (!ngx_exiting) {
    43                 ngx_exiting = 1;
    44                 ngx_close_listening_sockets(cycle);
    45                 ngx_close_idle_connections(cycle);
    46             }
    47         }
    48 
    49         if (ngx_reopen) {
    50             ngx_reopen = 0;
    51             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
    52             ngx_reopen_files(cycle, -1);
    53         }
    54     }
    55 }

    可以看出,里面除了exiting,terminate,quit和reopen等控制操作外,只有ngx_process_events_and_timers(src/event/ngx_event.c)。再看ngx_process_events_and_timers函数:

     1 void
     2 ngx_process_events_and_timers(ngx_cycle_t *cycle)
     3 {
     4     ngx_uint_t  flags;
     5     ngx_msec_t  timer, delta;
     6 
     7     if (ngx_timer_resolution) {
     8         timer = NGX_TIMER_INFINITE;
     9         flags = 0;
    10 
    11     } else {
    12         timer = ngx_event_find_timer(); 
    13         flags = NGX_UPDATE_TIME;
    14 
    15 #if (NGX_WIN32)
    16 
    17         /* handle signals from master in case of network inactivity */
    18 
    19         if (timer == NGX_TIMER_INFINITE || timer > 500) {
    20             timer = 500;
    21         }
    22 
    23 #endif
    24     }
    25 
    26     if (ngx_use_accept_mutex) {
    27         if (ngx_accept_disabled > 0) {
    28             ngx_accept_disabled--;
    29 
    30         } else {
    31             if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
    32                 return;
    33             }
    34 
    35             if (ngx_accept_mutex_held) {
    36                 flags |= NGX_POST_EVENTS;
    37 
    38             } else {
    39                 if (timer == NGX_TIMER_INFINITE
    40                     || timer > ngx_accept_mutex_delay)
    41                 {
    42                     timer = ngx_accept_mutex_delay;
    43                 }
    44             }
    45         }
    46     }
    47 
    48     delta = ngx_current_msec;
    49 
    50     (void) ngx_process_events(cycle, timer, flags);
    51 
    52     delta = ngx_current_msec - delta;
    53 
    54     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
    55                    "timer delta: %M", delta);
    56 
    57     ngx_event_process_posted(cycle, &ngx_posted_accept_events);
    58 
    59     if (ngx_accept_mutex_held) {
    60         ngx_shmtx_unlock(&ngx_accept_mutex);
    61     }
    62 
    63     if (delta) {
    64         ngx_event_expire_timers();
    65     }
    66 
    67     ngx_event_process_posted(cycle, &ngx_posted_events);
    68 }

    1.函数开头7-24行计算定时器timer,用于后面的ngx_process_events(50行)函数,最终用于各种I/O复用函数的timeout参数,如epoll_wait的timeout参数。ngx_process_events是一个宏,#define ngx_process_events   ngx_event_actions.process_events(src/event/ngx_event.h)。全局变量ngx_event_actions在各个event module的init方法里面设置,如ngx_epoll_init(src/event/modules/ngx_epoll_module.c)中ngx_event_actions = ngx_epoll_module_ctx.actions;。而各个module的init方法的调用需要通过Nginx指定使用哪种类型的module来设置。相关初始设置在ngx_event_core_init_conf(src/event/ngx_event.c)里面指定。

    2.63-65行处理timer超时事件。

    以epoll module为例,ngx_process_events最终指向ngx_epoll_process_events(src/event/modules/ngx_epoll_module.c)。

     1 static ngx_int_t
     2 ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
     3 {
     4    
     5     ......
     6     //将最近的timer作为epoll_wait的超时
     7     events = epoll_wait(ep, event_list, (int) nevents, timer);
     8 
     9     err = (events == -1) ? ngx_errno : 0;
    10 
    11     ......
    12     //处理全部事件
    13     for (i = 0; i < events; i++) {
    14         
    15        ......
    16 
    17         if ((revents & EPOLLIN) && rev->active) {
    18 
    19             rev->ready = 1;
    20 
    21             if (flags & NGX_POST_EVENTS) {
    22                 queue = rev->accept ? &ngx_posted_accept_events
    23                                     : &ngx_posted_events;
    24 
    25                 //将事件放入队列,稍后处理
    26                 ngx_post_event(rev, queue);
    27 
    28             } else {
    29                 rev->handler(rev);
    30             }
    31         }
    32 
    33         wev = c->write;
    34 
    35         if ((revents & EPOLLOUT) && wev->active) {
    36 
    37            ......
    38 
    39             wev->ready = 1;
    40 
    41             if (flags & NGX_POST_EVENTS) {
    42                 //将事件放入队列,稍后处理
    43                 ngx_post_event(wev, &ngx_posted_events);
    44 
    45             } else {
    46                 wev->handler(wev);
    47             }
    48         }
    49     }
    50 
    51     return NGX_OK;
    52 }

    可以看出,前面计算得到的timer传进了epoll_wait里面。其实这个timer的值是通过函数 ngx_event_find_timer从全部的timer组成的最小堆(Nginx里面使用RB tree)里面取出来的最小timer值。 epoll_wait之后就是常见的事件处理了。将事件放入队列主要是为快速释放accept锁,给其他worker进程机会去处理accept事件。

    注:引用本人文章请注明出处,谢谢。

  • 相关阅读:
    设计模式之-----------单例设计模式
    ubuntu 14 编译视频第三方库ijkplayer,能够在winows下使用
    AMP Physical Link Creation And Disconnect
    AnimationEvent事件问题
    网络事件触发自己主动登录
    Learn from Architects of Buildings
    对软件测试团队“核心价值”的思考
    【转】Android开发调试工具ADB的使用
    【转】adb控台中Permission denied的解决方案
    【转】蓝牙ble app开发(三) -- 抓包
  • 原文地址:https://www.cnblogs.com/NerdWill/p/4989859.html
Copyright © 2011-2022 走看看