Nginx 事件基本处理流程分析
说明:本文章重点关注事件处理模型。有兴趣的同学可以去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 事件基本处理流程分析的更多相关文章
- Nginx 多进程连接请求/事件分发流程分析
Nginx使用多进程的方法进行任务处理,每个worker进程只有一个线程,单线程循环处理全部监听的事件.本文重点分析一下多进程间的负载均衡问题以及Nginx多进程事件处理流程,方便大家自己写程序的时候 ...
- springBoot高级:自动配置分析,事件监听,启动流程分析,监控,部署
知识点梳理 课堂讲义 02-SpringBoot自动配置-@Conditional使用 Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载 ...
- nginx的请求接收流程(二)
在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,heade ...
- nginx的请求接收流程(一)
今年我们组计划写一本nginx模块开发以及原理解析方面的书,整本书是以open book的形式在网上会定时的更新,网址为http://tengine.taobao.org/book/index.htm ...
- freeswitch呼叫流程分析
今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...
- 【转】Hostapd工作流程分析
[转]Hostapd工作流程分析 转自:http://blog.chinaunix.net/uid-30081165-id-5290531.html Hostapd是一个运行在用户态的守护进程,可以通 ...
- Android7.0 Phone应用源码分析(二) phone来电流程分析
接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有 ...
- 从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)
退出Activity注册Android遍历 目录(?)[+] 前言 知识结构 具体方案 方案1 方法采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序多activity 方案2 方 ...
- ofbiz进击 。 ofbiz 退货流程(包含获取可退货项流程分析 以及 取消退货项的过程分析)
根据订单获取可退货项流程分析 退货的时候,调用 services_return.xml 中的获取可进行退货的退货项 getReturnableItems ,该服务调用了Java类 org.ofbi ...
随机推荐
- Unit02: JSON 、 使用JSON实现数据交换 、 jQuery对AJAX的支持,编码问题
Unit02: JSON . 使用JSON实现数据交换 . jQuery对AJAX的支持 1. 编码问题 (1)发送get请求 为什么会产生乱码? ie浏览器提供的ajax对象,对中文会使用gbk来编 ...
- linux 时钟时间,用户CPU时间,系统CPU时间 .
之前看过几次这几个的概念,但还是老是记不住,干脆就直接写下来,以后方便看~ 所谓的时钟时间又叫做墙上时钟时间,它是进程运行的时钟总量,其值与系统中同时运行的进程数有关,不过一般在讨论时钟时间的时候都是 ...
- Epel源配置
CentOS6的请编辑/etc/yum.repos.d/epel.repo输入以下内容 [epel] name=Extra Packages for Enterprise Linux 6 - $bas ...
- spring扩展点之一:BeanFactoryPostProcessor和BeanPostProcessor
一.BeanFactoryPostProcessor和BeanPostProcessor的区别 BeanFactoryPostProcessor和BeanPostProcessor都是spring初始 ...
- 【UVa】1601 The Morning after Halloween(双向bfs)
题目 题目 分析 双向bfs,对着书打的,我还调了好久. 代码 #include<cstdio> #include<cstring> #include<c ...
- 【C++】
C++声明function后面加上等于0(=0)何解? https://zhidao.baidu.com/question/1446181256925153340.html
- centos 安装php7
yum安装php7 删除之前的版本 # yum remove php* rpm 安装 Php7 相应的 yum源 CentOS/RHEL 7.x: # rpm -Uvh https://dl.fedo ...
- stm32通信概述
本文提到的内容有以下几个方面: 通信概述 串口通信 I2C通信 CAN通信 SPI通信 I2S通信 USB通信 其他通信 一.通信概述 按照数据传送方式分: 串行通信(一条数据线.适合远距离传输.控制 ...
- JQueryDOM节点操作
你一.JQueryDom节点操作 2.1查找节点 获取p节点 var $pDm=$("p"); 输出p节点的title属性 alert($pDm.attr("title& ...
- Ansible 快速上手
Ansible优点: 充分利用现有设施.使用 Ansible 无需安装服务端和客户端,只要 SSH 即可.这意味着,任何一台装有 Ansible 的机器都可以成为强大的管理端.我觉得,这种去中心化的思 ...