Nginx 的异步非阻塞体现在哪里?从理论分析到源码验证

定时器:nginx 借助 epoll_wait 的 timewait 设置超时时间,nginx 里面的定时器事件放在一颗维护定时器的红黑树里面,每次在进入epoll_wait 前,先从该红黑树里面拿到所有定时器事件的最小时间,在计算出 epoll_wait 的超时时间后进入 epoll_wait。所以,当没有事件产生,也没有中断信号时,epoll_wait会超时,也就是说,定时器事件到了。这时,nginx会检查所有的超时事件,将他们的状态设置为超时,然后再去处理网络事件。


源码体现


(看了半天,感觉是 reactor 模型,下一个项目我要用这个!)

worker进程对事件模块的初始化:

事件模块的初始化就发生在ngx_worker_process_init函数中。

其调用关系:

main()->

ngx_master_process_cycle()->

ngx_start_worker_processes()->

ngx_spawn_process()->

ngx_worker_process_cycle()->

ngx_worker_process_init()

在这个函数里面,调用了各个模块的启动方法:

for (i = 0; ngx_modules[i]; i++) {

if (ngx_modules[i]->init_process) {

if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {

/* fatal */

exit(2);

}

}

}

在此处,会调用ngx_event_core_module的ngx_event_process_init函数。

这个函数(很长,很长)主要做了这么几件事情:

1、处理惊群问题(回头会专门来一篇讲惊群)

2、初始化两个队列,一个用于存放不能及时处理的建立连接事件,一个用于存储不能及时处理的读写事件。

ngx_queue_init(&ngx_posted_accept_events);

ngx_queue_init(&ngx_posted_events);

3、初始化定时器。

4、调用 ngx_epoll_init 。

5、分配连接池空间、读事件结构体数组、写事件结构体数组。

6、为每个监听端口分配连接。

7、为每个监听端口的连接的读事件设置handler,并将每个监听端口的连接的读事件添加到epoll中。


worker 开始循环干活了

ngx_worker_process_cycle 函数(也不是很长,但是我不想放)。

不过这个我可以压缩一下,因为看了好几遍了,没上面那个那么恐怖:

for ( ;; ) {

/* 处理IO事件和时间事件 */

ngx_process_events_and_timers(cycle);

}

短哈。

在worker的主循环中,所有的事件都是通过函数ngx_process_events_and_timers处理的,那我们自然就要再往下走了嘛,今天我还非要看看它到底是怎么吃一半了再塞回去的!


ngx_process_events_and_timers

这个函数又干了些什么好事儿呢?

1、配置更新时间方式。是吧,原先我是不把定时器当回事儿的,但是这代码到处都是定时器,所以我就把定时器当回事儿了。

2、处理了一下惊群锁的事情。主要思想就是:负载过高咱就不抢,不然赶紧的冲上去。后面讲惊群的时候放这个代码。

3、调用事件处理函数ngx_process_events,epoll使用的是ngx_epoll_process_events函数。这个咱一会儿还得进去。

4、计算ngx_process_events函数的调用时间。

5、处理ngx_posted_accept_events队列的连接事件。这里accept事件的handler为ngx_event_accept。

6、处理定时器事件,具体操作是在定时器红黑树中查找过期的事件,调用其handler方法。

7、处理ngx_posted_events队列的读写事件,即遍历ngx_posted_events队列,调用事件的handler方法。


ngx_epoll_process_events

再坚持一会儿。。。

static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)

{

······

ngx_event_t *rev, *wev;

/* 调用epoll_wait,从epoll中获取发生的事件 */

events = epoll_wait(ep, event_list, (int) nevents, timer);

······

/* 处理epoll_wait返回为-1的情况 */

if (err) {

······

}

/* 若events返回为0,判断是因为epoll_wait超时还是其他原因 */

if (events == 0) {

if (timer != NGX_TIMER_INFINITE) {

return NGX_OK;

}

ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

“epoll_wait() returned no events without timeout”);

return NGX_ERROR;

}

/* 对epoll_wait返回的链表进行遍历 */

for (i = 0; i < events; i++) {

c = event_list[i].data.ptr;

/* 从data中获取connection & instance的值,并解析出instance和connection */

instance = (uintptr_t) c & 1;

c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);

/* 取出connection的read事件 */

rev = c->read;

/* 判断读事件是否过期 */

if (c->fd == -1 || rev->instance != instance) {

ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

“epoll: stale event %p”, c);

continue;

}

/* 取出事件的类型 */

revents = event_list[i].events;

ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

“epoll: fd:%d ev:%04XD d:%p”,

c->fd, revents, event_list[i].data.ptr);

/* 若连接发生错误,则将EPOLLIN、EPOLLOUT添加到revents中,在调用读写事件时能够处理连接的错误 */

if (revents & (EPOLLERR|EPOLLHUP)) {

ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

“epoll_wait() error on fd:%d ev:%04XD”,

c->fd, revents);

revents |= EPOLLIN|EPOLLOUT;

}

/* 事件为读事件且读事件在epoll中 */

if ((revents & EPOLLIN) && rev->active) {

······

rev->ready = 1;

/* 事件是否需要延迟处理?对于抢到锁监听端口的worker,会将事件延迟处理 */

if (flags & NGX_POST_EVENTS) {

/* 根据事件的是否是accept事件,加到不同的队列中 */

queue = rev->accept ? &ngx_posted_accept_events
&ngx_posted_events;

ngx_post_event(rev, queue);

} else {

/* 若不需要延迟处理,直接调用read事件的handler */

rev->handler(rev);

}

}

/* 取出connection的write事件 */

wev = c->write;

/* 事件为写事件且写事件在epoll中 */

if ((revents & EPOLLOUT) && wev->active) {

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
外链图片转存中…(img-TvGw4Lm1-1715855542369)]

[外链图片转存中…(img-D4hMtmEj-1715855542369)]

[外链图片转存中…(img-UGb4e3Mp-1715855542370)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值