微信公众号:郑尔多斯
关注可了解更多的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出现异常(被复用),所以现在看来,我们担心的那个问题是多虑了。


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

郑尔多斯

转载于:https://www.cnblogs.com/zhengerduosi/p/10186358.html

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

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

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

  2. Nginx 模块开发高级篇

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

  3. Python开发【第六篇】:模块

    Python开发[第六篇]:模块 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一 ...

  4. CCIE-LAB-第十六篇-NAT+OSPF下发默认路由+校验配置(模块一结束篇章)

    CCIE-LAB-第十六篇-NAT+OSPF下发默认路由+校验配置(模块一结束篇章) 实际中,思科只会给你5个小时去做下面的全部配置 这个是CCIE-LAB的拓扑图 问题 翻译: 根据这些要求1为FA ...

  5. Pygame Event事件模块(示例)

    Pygame Event事件模块(示例) 原文链接 参考文章 事件(Event)是 Pygame 的重要模块之一,它是构建整个游戏程序的核心,比如鼠标点击.键盘敲击.游戏窗口移动.调整窗口大小.触发特 ...

  6. Pygame Event事件模块(详细示例)

    Pygame Event事件模块(详细示例) 事件(Event)是 Pygame 的重要模块之一,它是构建整个游戏程序的核心,比如鼠标点击.键盘敲击.游戏窗口移动.调整窗口大小.触发特定的情节.退出游 ...

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

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

  8. Zookeeper+Hadoop+Hbase+Hive+Kylin+Nginx集群搭建六(Kylin篇)

    Zookeeper+Hadoop+Hbase+Hive+Kylin+Nginx集群搭建六(kylin篇) 八.Kylin搭建 1.安装 2.配置环境变量 3.配置kylin 4.启动使用 5.问题 全 ...

  9. jquery源码分析(七)——事件模块 event(二)

    上一章节探讨了事件的一些概念,接下来看下jQuery的事件模块. jQuery对事件的绑定分别有几个API:.bind()/.live()/.delegate()/.on()/click(), 不管是 ...

最新文章

  1. Zynq ROM 加载Mode 图表
  2. linux fedora14 u盘运行,用U盘安装Fedora的详细步骤
  3. 再写堆(堆的性质,向下调整,建堆,堆的插入删除初始化,堆排序,TopK问题)
  4. vue中的方法如何暴露给webview,使安卓和iOS可以调用
  5. (XGBoost)提升树入门介绍(Inrtoduction to Boosted Trees)
  6. android pie华为更新,华为多款机型近期将迎来Android Pie系统更新,下列这几款肯定有你...
  7. 26. Postpone variable definitions as long as possible
  8. 运行 Visual Studio 2019当前页面的脚本发生错误解决方法
  9. Jquery插件ajaxFileUpload文件上传与Bootstrap之fileinput插件上传文件的使用与MultipartFile使用与导入Excel和导出Excel
  10. 明星造型师Tara Swennen和Glamhive创始人Stephanie Sprangers再次汇聚全球造型、时尚、美妆和女性赋权领域“大咖”参加突破性数字盛会
  11. 一个服务器放多个网站seo,服务器建立多个网站 同一IP对SEO有影响吗
  12. Citespace(十一)---对图谱中Q值和S值的说明—来自《引文空间分析原理与应用》p24
  13. 不谋全局者 , 不足以谋一域 : 不谋万世者 , 不足以谋一时
  14. 美团App页面视图可测性改造实践
  15. bsearch()快搜函数
  16. Arcgis动态图制作——以全国各省情绪变化地图为例
  17. 【Red Hat7创建/扩容/删除逻辑卷 #步骤非常非常详细】
  18. JS自动播放视频脚本
  19. MapReduce之多MapReduce执行
  20. 安卓设置均衡器 Equalizer

热门文章

  1. c 取oracle 错误代码,转载ORA-01591错误故障处理
  2. react大数据量渲染_UseEffect在React中运行无限渲染(超过最大更新深度)
  3. 运行VINS-mono:/home/tony-ws1/output/pose_graph/ not exists, trying to create it /home/tony-ws1/ou错误解决
  4. OpenCV4每日一练day8:模型投影函数projectPoints()
  5. java 弹出下载_java怎么实现下载弹出的对话框
  6. linux内核实时调度,基于Linux内核的实时调度机制的研究和实现
  7. python聚类分析散点图_使用sklearn对iris数据集进行聚类分析
  8. python numba报错_python – 使用pip升级到numba 0.16会导致错误
  9. 系统地址和服务器地址不一致,在打开远程通服务配置程序时检测系统配置时提示配置失败,映射的ip地址与服务器不一致。...
  10. python行数据切片_通过切片访问DataFrame行