微信公众号:郑尔多斯
关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;
关注公众号,有趣有内涵的文章第一时间送达!

本文章原作者aweth0me,原文地址在这里点?我啊,我做了简单的修改和整理

内容主题

本文分析了instance的作用

什么是 stale event

If you use an event cache or store all the fd’s returned from epoll_wait(2), then make sure to provide a way to mark its closure dynamically (ie: caused by a previous event’s processing). Suppose you receive 100 events from epoll_wait(2), and in event #47 a condition causes event #13 to be closed. If you remove the structure and close() the fd for event #13, then your event cache might still say there are events waiting for that fd causing confusion.One solution for this is to call, during the processing of event 47,epoll_ctl(EPOLL_CTL_DEL) to delete fd 13 and close(), then mark its associated data structure as removed and link it to a cleanup list. If you find another event for fd 13 in your batch processing, you will discover the fd had been previously removed and there will be no confusion.

上面的意思简单的翻译一下如下:

如果你把epoll_wait()返回的文件描述符进行了cache,那么你必须动态监测我们cache的文件描述符是否被关闭了。设想有这么一种情况,epoll_wait()一次返回了100个准备就绪的事件,然后再第#47号事件的处理函数中,关闭了第#13个事件。如果你把第#13号事件对应的结构体删除,并且把#13号事件对应的fd关闭,但是你缓存的event cache在处理到这个事件的时候就会比较郁闷了。一个解决的方案就是,在处理#47号事件的时候,使用epoll_ctl()关闭#13号事件对应的fd,然后把#13号事件对应的数据结构放到cleanup list链表中。

Nginx 如何处理

nginx ngx_event_t结构中的instance变量是处理stale event的核心,这里值得一提的是,connection连接池(实际是个数组)和event数组(分readwrite)。他们都是在初始化时就固定下来,之后不会动态增加和释放,请求处理中只是简单的取出和放回。而且有个细节就是,假设connection数组的大小为n,那么read event数组和write event数组的数量同样是n,数量上一样,每个连接对应一个readwrite event结构,在链接被回收的时候,他们也就不能使用了。
我们看看连接池取出和放回的动作:
先看放回,一个请求处理结束后,会通过ngx_free_connection将其持有的连接结构还回连接池,动作很简单:

1c->data = ngx_cycle->free_connections;2ngx_cycle->free_connections = c;3ngx_cycle->free_connection_n++;复制代码

然后看下面的代码:

 1/* 2这里要主要到的是,c结构体中并没有清空,各个成员值还在(除了fd被置为-1外),那么新请求在从连接池里拿连接时,获得的结构都还是没用清空的垃圾数据,我们看取的时候的细节: 3*/ 4 5// 此时的c含有没用的“垃圾”数据 6c = ngx_cycle->free_connections; 7...... 8// rev和wev也基本上“垃圾”数据,既然是垃圾,那么取他们还有什么用?其实还有点利用价值。。 9rev = c->read;10wev = c->write;1112// 现在才清空c,因为后面要用了,肯定不能有非数据存在,从这里我们也多少可以看得出,nginx13// 为什么在还的时候不清,我认为有两点:尽快还,让请求有连接可用;延迟清理,直到必须清理时。14// 总的来说还是为了效率。15ngx_memzero(c, sizeof(ngx_connection_t));1617c->read = rev;18c->write = wev;19c->fd = s;2021// 原有event中的instance变量22instance = rev->instance;2324// 这里清空event结构25ngx_memzero(rev, sizeof(ngx_event_t));26ngx_memzero(wev, sizeof(ngx_event_t));2728// 新的event中的instance在原来的基础上取反。意思是,该event被重用了。因为在请求处理完29// 之前,instance都不会被改动,而且当前的instance也会放到epoll的附加信息中,即请求中event30// 中的instance跟从epoll里得到的instance是相同的,不同则是异常了,需要处理。31rev->instance = !instance;32wev->instance = !instance;复制代码

现在我们实际的问题:ngx_epoll_process_events

 1// 当前epoll上报的事件挨着处理,有先后。 2    for (i = 0; i < events; i++) { 3        c = event_list[i].data.ptr; 4 5        // 难道epoll中附加的instance,这个instance是在刚获取连接池时已经设置的,一般是不会变化的。 6        instance = (uintptr_t) c & 1; 7        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); 8 9        // 处理可读事件10        rev = c->read;11        /*12          fd在当前处理时变成-1,意味着在之前的事件处理时,把当前请求关闭了,13          即close fd并且当前事件对应的连接已被还回连接池,此时该次事件就不应该处理了,作废掉。14          其次,如果fd > 0,那么是否本次事件就可以正常处理,就可以认为是一个合法的呢?答案是否定的。15          这里我们给出一个情景:16          当前的事件序列是: A ... B ... C ...17          其中A,B,C是本次epoll上报的其中一些事件,但是他们此时却相互牵扯:18          A事件是向客户端写的事件,B事件是新连接到来,C事件是A事件中请求建立的upstream连接,此时需要读源数据,19          然后A事件处理时,由于种种原因将C中upstream的连接关闭了(比如客户端关闭,此时需要同时关闭掉取源连接),自然20          C事件中请求对应的连接也被还到连接池(注意,客户端连接与upstream连接使用同一连接池),21          而B事件中的请求到来,获取连接池时,刚好拿到了之前C中upstream还回来的连接结构,当前需要处理C事件的时候,22          c->fd != -1,因为该连接被B事件拿去接收请求了,而rev->instance在B使用时,已经将其值取反了,所以此时C事件epoll中23          携带的instance就不等于rev->instance了,因此我们也就识别出该stale event,跳过不处理了。24         */25        if (c->fd == -1 || rev->instance != instance) {2627            /*28             * the stale event from a file descriptor29             * that was just closed in this iteration30             */3132            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,33                           "epoll: stale event %p", c);34            continue;35        }3637        /*38          我们看到在write事件处理时,没用相关的处理。事实上这里是有bug的,在比较新的nginx版本里才被修复。39          国内nginx大牛agent_zh,最早发现了这个bug,在nginx forum上有Igor和他就这一问题的讨论:40          http://forum.nginx.org/read.php?29,217919,218752         41         */4243        ......44    }复制代码

补充:
为什么简单的将instance取反,就可以有效的验证该事件是否是stale event?会不会出现这样的情况:
事件序列:ABB'C,其中A,B,C跟之前讨论的情形一样,我们现在很明确,B中获得A中释放的连接时,会将instance取反,这样在C中处理时,就可以发现rev->instance != instance,从而发现stale event。那么我们假设B中处理时,又将该connection释放,在B'中再次获得,同样经过instance取反,这时我们会发现,instance经过两次取反时,就跟原来一样了,这就不能通过fd == -1rev->instance != instance的验证,因此会当做正常事件来处理,后果很严重!
不知道看到这里的有没有跟我有同样的想法同学,其实这里面有些细节没有被抖出来,实际上,这里是不会有问题的,原因如下:
新连接通过accept来获得,即函数ngx_event_accept。在这个函数中会ngx_get_connection,从而拿到一个连接,然后紧接着初始化这个连接,即调用ngx_http_init_connection,在这个函数中通常是会此次事件挂到post_event链上去:

1if (ngx_use_accept_mutex) {2    ngx_post_event(rev, &ngx_posted_events);3    return;4}复制代码

然后继续accept或者处理其他事件。而一个进程可以进行accept,必然是拿到了进程间的accept锁。凡是进程拿到accept锁,那么它就要尽快的处理事务,并释放锁,以让其他进程可以accept,尽快处理的办法就是将epoll此次上报的事件,挂到响应的链表或队列上,等释放accept锁之后在自己慢慢处理。所以从epoll_wait返回到外层,才会对post的这些事件来做处理。在正式处理之前,每个新建的连接都有自己的connection,即BB'肯定不会在connection上有任何搀和,在后续的处理中,对C的影响也只是由于BB'从连接池中拿到了本应该属于Cconnection,从而导致fd(被关闭)和instance出现异常(被复用),所以现在看来,我们担心的那个问题是多虑了。


喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达

郑尔多斯

nginx事件 -- 第六篇 stale event相关推荐

  1. nginx事件模块 -- 第六篇 stale event

    微信公众号:郑尔多斯 关注可了解更多的Nginx知识.任何问题或建议,请公众号留言; 关注公众号,有趣有内涵的文章第一时间送达! 本文章原作者aweth0me,原文地址在这里点?我啊,我做了简单的修改 ...

  2. Noah Mt4跟单系统制作第六篇 Mt4TradeApi交易事件篇

    Noah Mt4跟单系统制作第六篇 Mt4TradeApi交易事件篇 using System; using System.Collections.Generic; using System.Linq ...

  3. 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...

  4. 用仿ActionScript的语法来编写html5——第六篇,TextField与输入框

    用仿ActionScript的语法来编写html5--第六篇,TextField与输入框 一,对比 1,html5中 首先看看在html5的canvas中的文字显示 var canvas = docu ...

  5. c# 委托和事件(总结篇)

    c# 委托和事件(总结篇) 概念: delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类.与其它的类不同,delegate类能够 拥有一个签名(signature),并且它& ...

  6. 秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据

    本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/mo ...

  7. java监听数据库操作_第十六篇——JDBC操作数据库之监听器

    JavaWeb应用中,很多的地方都和session有关.因此session相关的事件监听器,在日常工作中非常有用. 有时候我们需要统计当前在线的人数和访问人数总数,此时就可以使用监听器技术来很简单的实 ...

  8. Nginx 模块开发高级篇

    Nginx 模块开发高级篇 变量 综述 在Nginx中同一个请求需要在模块之间数据的传递或者说在配置文件里面使用模块动态的数据一般来说都是使用变量,比如在HTTP模块中导出了host/remote_a ...

  9. 小学计算机管理员教学计划,小学教学计划汇总六篇

    小学教学计划汇总六篇 日子如同白驹过隙,不经意间,我们又将学习新的知识,有新的感受,做好教学计划,让自己成为更有竞争力的人吧.教学计划怎么写才能切实地帮助到自己将来的工作呢?下面是小编整理的小学教学计 ...

最新文章

  1. python跟php如何共用mysql_Python 3 多个函数共用一个mysql连接
  2. Spring boot 如何读取配置文件properties中的信息
  3. git revert 的问题
  4. php编写开机启动脚本,设置 msyql php-fpm 开机自动启动脚本
  5. Android 查看设备文件
  6. python3.6入门到高阶(全栈) day015 初识面向对象
  7. 计算机基础:多媒体基础知识笔记
  8. [USACO 1.3.3]Calf Flac
  9. LeetCode 2070. 每一个查询的最大美丽值(离线查询+排序+优先队列)
  10. Linux debian设置主机不休眠教程
  11. 【英语学习】【Level 07】U02 Live Work L1 Welcome to my home
  12. ORA-12518: TNS: 监听程序无法分发客户机连接
  13. 金立手机又推新品!翻盖手机A326发布 意图复刻经典
  14. java实现加权抽样_用Java替换加权采样
  15. 聚合天气--ajax 通过城市名取数据
  16. C#从新浪新闻上提取新闻标题
  17. 第二十四课、布局管理器(三)
  18. POJ - 1384 Piggy-Bank(完全背包)
  19. Microsoft Office Visio 2007 简体中文专业版
  20. 整体改革理论(简介)

热门文章

  1. 【LeetCode 69】Sqrt(x)
  2. 九度OJ1005题 一直WA??
  3. 仿新浪邮件输入自动提示jQuery插件
  4. java swing 代码_java swing编写gui生命游戏代码,新手上路
  5. mysql user表字段详解_Mysql User表权限字段说明全介绍
  6. shiro 升级_小米11消息再次确认:骁龙875有独占期,超广角镜头大升级
  7. 思科服务器远程管理,Telnet远程访问思科交换机、路由器 TCP协议分析工具
  8. 程序员怒斥:阿里如此嚣张?为所欲为?谁让你动了我的浏览器
  9. 最全JavaScript基础总结~建议收藏
  10. web前端的进阶路线大剖析!初学者如何迅速“升级”!