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

程序流程图:

说明:

一、进程生成顺序

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, , "exiting");
  21.  
  22. ngx_worker_process_exit(cycle);
  23. }
  24. }
  25.  
  26. ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, , "worker cycle");
  27.  
  28. ngx_process_events_and_timers(cycle);
  29.  
  30. if (ngx_terminate) {
  31. ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "exiting");
  32.  
  33. ngx_worker_process_exit(cycle);
  34. }
  35.  
  36. if (ngx_quit) {
  37. ngx_quit = ;
  38. ngx_log_error(NGX_LOG_NOTICE, cycle->log, ,
  39. "gracefully shutting down");
  40. ngx_setproctitle("worker process is shutting down");
  41.  
  42. if (!ngx_exiting) {
  43. ngx_exiting = ;
  44. ngx_close_listening_sockets(cycle);
  45. ngx_close_idle_connections(cycle);
  46. }
  47. }
  48.  
  49. if (ngx_reopen) {
  50. ngx_reopen = ;
  51. ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "reopening logs");
  52. ngx_reopen_files(cycle, -);
  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 = ;
  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 > ) {
  20. timer = ;
  21. }
  22.  
  23. #endif
  24. }
  25.  
  26. if (ngx_use_accept_mutex) {
  27. if (ngx_accept_disabled > ) {
  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, ,
  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 == -) ? ngx_errno : ;
  10.  
  11. ......
  12. //处理全部事件
  13. for (i = ; i < events; i++) {
  14.  
  15. ......
  16.  
  17. if ((revents & EPOLLIN) && rev->active) {
  18.  
  19. rev->ready = ;
  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 = ;
  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事件。

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

Nginx 事件基本处理流程分析的更多相关文章

  1. Nginx 多进程连接请求/事件分发流程分析

    Nginx使用多进程的方法进行任务处理,每个worker进程只有一个线程,单线程循环处理全部监听的事件.本文重点分析一下多进程间的负载均衡问题以及Nginx多进程事件处理流程,方便大家自己写程序的时候 ...

  2. springBoot高级:自动配置分析,事件监听,启动流程分析,监控,部署

    知识点梳理 课堂讲义 02-SpringBoot自动配置-@Conditional使用 Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载 ...

  3. nginx的请求接收流程(二)

    在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,heade ...

  4. nginx的请求接收流程(一)

    今年我们组计划写一本nginx模块开发以及原理解析方面的书,整本书是以open book的形式在网上会定时的更新,网址为http://tengine.taobao.org/book/index.htm ...

  5. freeswitch呼叫流程分析

    今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...

  6. 【转】Hostapd工作流程分析

    [转]Hostapd工作流程分析 转自:http://blog.chinaunix.net/uid-30081165-id-5290531.html Hostapd是一个运行在用户态的守护进程,可以通 ...

  7. Android7.0 Phone应用源码分析(二) phone来电流程分析

    接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有 ...

  8. 从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)

      退出Activity注册Android遍历   目录(?)[+] 前言 知识结构 具体方案 方案1 方法采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序多activity 方案2 方 ...

  9. ofbiz进击 。 ofbiz 退货流程(包含获取可退货项流程分析 以及 取消退货项的过程分析)

    根据订单获取可退货项流程分析 退货的时候,调用 services_return.xml 中的获取可进行退货的退货项  getReturnableItems  ,该服务调用了Java类 org.ofbi ...

随机推荐

  1. javascript系列学习----Creating objects

    在javascript语言里面,一切皆是对象,对象是它的灵魂,值得我们学习和领悟对象的概念和使用,下面我会引用实例来进行说明. 1)创建对象 方法一:js的对象方法构造 var cody = new ...

  2. 登录MySQL非默认3306端口号的语句

    这里登陆的是mysql3308端口号的数据库 mysql -P3308 -p用户名 -u密码

  3. 使用sigaction来取代signal作为信号处理器函数

    早期ISO C提供了像这样的函数来支持自定义信号处理 typedef void (*sighandler)(int); sighandler signal(sighandler func); 但是由于 ...

  4. 嵌入式设备snmpd 移植和测试(78K为例)

    一.源码目录项内容 agent:编译snmpd程序所需的文件,mibs源文件: apps:其他相关的程序:snmpwalk,snmptrap,snmptest,snmpget,snmpset: mib ...

  5. 【转】用JMeter来测试Tomcat的性能

    JMeter是Apache组织的开放源代码项目,它是功能和性能测试的工具,100%的用java实现,最新的版本是1.9.1,大家可以到http://jakarta.apache.org/jmeter/ ...

  6. 操作系统-服务器-百科:Windows Server

    ylbtech-操作系统-服务器-百科:Windows Server Windows Server是微软在2003年4月24日推出的Windows 的服务器操作系统,其核心是Microsoft Win ...

  7. 阿里云安装 virtio 驱动

    为避免部分服务器.虚拟机或者云主机的操作系统在阿里云控制台 导入镜像 后,使用该自定义镜像创建的 ECS 实例无法启动,您需要在导入镜像前检查是否需要在源服务器中安装 Xen(pv)或 virtio ...

  8. 【BZOJ】2809: [Apio2012]dispatching(左偏树)

    题目 传送门:QWQ 分析 显然是一个资瓷合并的堆 现学了一发左偏树:教程 然后就没了 代码 #include <bits/stdc++.h> #define lc son[x][0] # ...

  9. svn删除

    [本地删除.然后提交到服务器]与[服务器删除然后本地更新] svn delete svn://路径(目录或文件的全路径) -m “删除备注信息文本” 推荐如下操作: svn delete 文件名 sv ...

  10. python学习——练习题(11)

    """ 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1 1 2 ...