说明:本文章重点关注事件处理模型。有兴趣的同学可以去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如下:

 static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
ngx_int_t worker = (intptr_t) data; ngx_process = NGX_PROCESS_WORKER;
ngx_worker = worker; ngx_worker_process_init(cycle, worker); ngx_setproctitle("worker process"); for ( ;; ) { if (ngx_exiting) {
ngx_event_cancel_timers(); if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
{
ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "exiting"); ngx_worker_process_exit(cycle);
}
} ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, , "worker cycle"); ngx_process_events_and_timers(cycle); if (ngx_terminate) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "exiting"); ngx_worker_process_exit(cycle);
} if (ngx_quit) {
ngx_quit = ;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, ,
"gracefully shutting down");
ngx_setproctitle("worker process is shutting down"); if (!ngx_exiting) {
ngx_exiting = ;
ngx_close_listening_sockets(cycle);
ngx_close_idle_connections(cycle);
}
} if (ngx_reopen) {
ngx_reopen = ;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "reopening logs");
ngx_reopen_files(cycle, -);
}
}
}

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

 void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta; if (ngx_timer_resolution) {
timer = NGX_TIMER_INFINITE;
flags = ; } else {
timer = ngx_event_find_timer(); 
flags = NGX_UPDATE_TIME; #if (NGX_WIN32) /* handle signals from master in case of network inactivity */ if (timer == NGX_TIMER_INFINITE || timer > ) {
timer = ;
} #endif
} if (ngx_use_accept_mutex) {
if (ngx_accept_disabled > ) {
ngx_accept_disabled--; } else {
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
} if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS; } else {
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
} delta = ngx_current_msec; (void) ngx_process_events(cycle, timer, flags); delta = ngx_current_msec - delta; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"timer delta: %M", delta); ngx_event_process_posted(cycle, &ngx_posted_accept_events); if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
} if (delta) {
ngx_event_expire_timers();
} ngx_event_process_posted(cycle, &ngx_posted_events);
}

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)。

 static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{ ......
//将最近的timer作为epoll_wait的超时
events = epoll_wait(ep, event_list, (int) nevents, timer); err = (events == -) ? ngx_errno : ; ......
//处理全部事件
for (i = ; i < events; i++) { ...... if ((revents & EPOLLIN) && rev->active) { rev->ready = ; if (flags & NGX_POST_EVENTS) {
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events; //将事件放入队列,稍后处理
ngx_post_event(rev, queue); } else {
rev->handler(rev);
}
} wev = c->write; if ((revents & EPOLLOUT) && wev->active) { ...... wev->ready = ; if (flags & NGX_POST_EVENTS) {
//将事件放入队列,稍后处理
ngx_post_event(wev, &ngx_posted_events); } else {
wev->handler(wev);
}
}
} return NGX_OK;
}

可以看出,前面计算得到的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. ASP.NET-自定义HttpModule与HttpHandler介绍

    ASP.NET对请求处理的过程:当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给 ASPNET_ISAPI.dll,A ...

  2. 将socket程序从linux移植到windows上

    今天突然想试下纯socket编程在两个系统上代码重合量有多大,只要不使用VC自定义的宏(比如SOCKET.SOCKADDR等等)感觉代码重合量挺大的. 比如最简单的TCP客户端和服务端对话,在VC中用 ...

  3. zufeoj NO.1(结构体简单题)

    NO.1 时间限制: 1 Sec  内存限制: 128 MB提交: 457  解决: 172[提交][状态][讨论版] 题目描述 所谓NO.1,就是所有成绩都排在第一的同学,我们假设每个人只有理科,文 ...

  4. springmvc和activemq的整合使用

    1.简介:ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线.ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台 ...

  5. 4_python之路之模拟工资管理系统

    python之路之模拟工资管理系统 1.程序说明:Readme.txt 1.程序文件:salary_management.py info.txt 2.程序文件说明:salary_management. ...

  6. webDriver对element进行操作

    非常感谢原作者:eastmount,原地址:http://blog.csdn.net/eastmount/article/details/48108259     感谢感谢 这篇文章主要Seleniu ...

  7. 梯度下降(Gradient descent)

    首先,我们继续上一篇文章中的例子,在这里我们增加一个特征,也即卧室数量,如下表格所示: 因为在上一篇中引入了一些符号,所以这里再次补充说明一下: x‘s:在这里是一个二维的向量,例如:x1(i)第i间 ...

  8. node / npm 配置问题

    安装nodejs 后运行 npm 命令无响应处理方法 安装和卸载过nodejs, 也编辑过 C:\Users\{账户}\下的.npmrc文件. 再全新安装nodejs ,运行npm 命令,无响应. 处 ...

  9. Zabbix 源码编译安装

    简介: Zabbix 分布式监控系统,源码编译安装记录 ( 记不得是第多少次了 ) 下载地址:http://jaist.dl.sourceforge.net/project/zabbix/ZABBIX ...

  10. 复制新增树状数据_ParentID也需要更新

    CREATE TABLE TreeShip(  [ID] uniqueidentifier PRIMARY KEY NOT NULL,  [Name] [nvarchar](50) NULL,  [R ...