Nginx源码分析—过期事件和惊群事件的处理

过期事件:每个事件的date域都是一个结构体ngx_connection_t结构体,表示对应的连接。对于一个结构体struct epoll_event 中的data.ptr成员存储的是ngx_connection_t连接,这里使用Instance标志位来标识,下面就配合ngx_epoll_process_events方法说明他的用法。

Data.ptr (void* )((uintptr_t) c  |  ev->instance); 这事添加事件的使用对ptr进行的初始化。

那么在检查的时候怎么使用它呢?

遍历epoll_wait返回的所有事件,对照上面提到的ngx_epoll_add_event方法,可以看到ptr成员就是ngx_connection_t连接的地址,但最后一位有特殊含义,需要把他屏蔽到。

C= event_list[i].data.ptr;

//将地址的最后一位取出来,用Instace变量标识

Instance= (uintptr_t) c &1;

/*五路是32位还是64位机器,其地址的最后一位肯定是0.可以用下面的这行语句把ngx_connection_t的地址还原到真正的地址值*/

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

//取出事件

Rev= c->read;

//判断这个度事件是否为过期事件

If(c->fd== -1 || wev->instance != instance)

{

//当fd套接字描述符为-1 或者Instance标志位不相等是,表示这个事件已经过期了,不用处理

Instance标志位为什么可以判断事件是否过期?从上面的代码可以看出,instance标志位的使用其实很简单,它利用了指针的最后一位是0这个特性,既然最后一位始终为0,那么不用不如用来表示Instance,这样,在使用ngx_epoll_add_event方法向epoll中添加事件时,就把epoll_event中联合成员data的ptr成员指向ngx_connection_t连接的地址,同时把最后一位置为这个事件的Instance标志。而在ngx_epoll_process_events方法中取出指向连接的ptr地址时,先把最后一位instance取出来,再把ptr还原成正常的地址赋给ngx_connection_t连接,这样,Instance究竟放在何处的问题也就解决了

那么,过期事件又是怎么回事呢?举个例子,epoll_wait一次放回3个事件,在第一个事件的处理过程总,由于业务的需要,所以关闭了一个连接,而这个连接恰好对应第三个事件,这样的话,在处理到第三个事件时,这个时间就已经是过期事件了,一旦处理必然出错。既然如此,把关闭的这个连接的fd套接字置为-1能解决问题么?答案是不能处理所有出错。

假设第三个事件对应的Ngx_connection_t连接中的fd套接字原先是50,处理第一个事件时把这个连接的套接字关闭了,同时置为-1,并且调用ngx_free_connection将该连接归还给连接池,在Ngx_epoll_process_events方法的循环中开始处理第二个事件,恰好第二个事件时建立新连接诶事件,调用ngx_get_connection从连接池中取出的连接非常可能及时刚才释放的第三个事件对应的连接,由于套接字50刚刚被释放,Linux内核非常有可能把刚刚释放的套接字50又分配给新建立的连接。因此,在循环中处理第三个事件时,这个事件就是过期了,它对应的事件时关闭的连接,而不是新建立的连接。

如何解决这个问题?依靠Instance标志位,当调用ngx_get_connection从连接池中获取一个新连接时,Instance标志位就会置反,

Ngx_get_connection(s,log)

{

Rev = c->read;

Wev = c->write;

Instane = rev->instance;

//将instance 标志位置为原来的相反值

Rev->instance = !instance;

Wev->instance = !instance;

}

这样,当这个ngx_connection_t连接重复使用时,他的instance标志位一定是不同的。因此,在ngx_epoll_process_events方法中一旦判断instance发生了变化,就认为这是过期事件而不处理。

时间: 2024-10-10 03:47:36

Nginx源码分析—过期事件和惊群事件的处理的相关文章

Nginx源码分析:3张图看懂启动及进程工作原理

编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」.   导读:很多工程师及架构师都希望了解及掌握高性能服务器开发,阅读优秀源代码是一种有效的方式,nginx 是业界知名的高性能 Web 服务器实现,如何有效的阅读及理解 nginx?本文用图解的方式帮助大家来更好的阅读及理解 nginx 关键环节的实现.   陈科,十年行业从业经验,曾在浙江电信.阿里巴巴.华为.五八同城任开发工程及架构师等职,目前负责河狸

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选

nginx源码分析--模块分类

ngx-modules Nginx 主要的模块大致可以分为四类: handler – 协同完成客户端请求的处理.产生响应数据.比如模块, ngx_http_rewrite_module, ngx_http_log_module, ngx_http_static_module. filter – 对 handler 产生的响应数据做各种过滤处理.比如模块, ngx_http_not_modified_filter_module, ngx_http_header_filter_module. ups

nginx源码分析--ngx_http_optimize_servers()函数

这个函数做了连部分工作:1)以端口为入口点 将有用的信息存放到hash表内 2)调用ngx_http_init_listening()函数 对端口进行监听 1. 在ngx_http_core_main_conf_t结构体中有一个字段为ports,是一个数组,数组内存放的全是ngx_http_conf_port_t:对于每一个端口信息(ngx_http_conf_port_t),调用 ngx_http_server_names函数,同时也调用ngx_http_init_listening函数,这里

nginx源码分析--监听套接字的创建 套接字的监听 HTTP请求创建连接

作为一个web服务器,那么肯定是有监听套接字的,这个监听套接字是用于接收HTTP请求的,这个监听套接字的创建是根据配置文件的内容来创建的,在nginx.conf文件中有多少个地址就需要创建多少个监听套接字.这里不说各个结构体的构造 只说大体情况! 1).首先在main函数中调用了ngx_init_cycle()函数,在这个函数的最后调用了ngx_open_listening_sockets函数,这个函数负责将创建的监听套接字进行套接字选项的设置(比如非阻塞.接受发送的缓冲区.绑定.监听处理) 2

nginx源码分析--GDB调试

利用gdb[i]调试nginx[ii]和利用gdb调试其它程序没有两样,不过nginx可以是daemon程序,也可以以多进程运行,因此利用gdb调试和平常会有些许不一样.当然,我们可以选择将nginx设置为非daemon模式并以单进程运行,而这需做如下设置即可: daemon off; master_process off; 这是第一种情况: 这种设置下的nginx在gdb下调试很普通,过程可以[iii]是这样: 执行命令: [email protected]:/usr/local/nginx/

Nginx源码分析 - Nginx启动以及IOCP模型

Nginx 源码分析 - Nginx启动以及IOCP模型 版本及平台信息 本文档针对Nginx1.11.7版本,分析Windows下的相关代码,虽然服务器可能用linux更多,但是windows平台下的代码也基本相似 ,另外windows的IOCP完成端口,异步IO模型非常优秀,很值得一看. Nginx启动 曾经有朋友问我,面对一个大项目的源代码,应该从何读起呢?我给他举了一个例子,我们学校大一大二是在紫金港校区,到了 大三搬到玉泉校区,但是大一的时候也会有时候有事情要去玉泉办.偶尔会去玉泉,但

nginx源码分析--进程间通信机制 & 同步机制

Nginx源码分析-进程间通信机制 从nginx的进程模型可以知道,master进程和worker进程需要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号,master进程主要就是监控.接受外部信号,将有必要的信号传递给worker进程,master进程大部分时间都是阻塞在sigsuspend()函数调用上.Worker进程屏蔽了所有的外部信号,那么Master进程就通过套接字和worker进程通信,worker进程修改全局变量,使得worker进程接受

nginx源码分析--从源码看nginx框架总结

nginx源码总结: 1)代码中没有特别绕特别别扭的编码实现,从变量的定义调用函数的实现封装,都非常恰当,比如从函数命名或者变量命名就可以看出来定义的大体意义,函数的基本功能,再好的架构实现在编码习惯差的人实现也会黯然失色,如果透彻理解代码的实现,领悟架构的设计初衷,觉得每块代码就想经过耐心雕琢一样,不仅仅实现了基本的功能给你,为其他人阅读也会提供很好的支持.细致恰当的命名规则就可以看出作者的功力. 2)更好更高的软件性能体现在架构设计上,好的架构会让软件更加稳定.容易维护.便于扩展.从核心模块