instance标志为什么可以判断事件是否过期?
(1)放在何处 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连接。
(2)过期事件又是怎么回事?
举个例子,加入epoll_wait一次返回3个事件,在第一个事件的处理过程中,由于业务的需要,所以关闭了一个连接,而这个连接恰好对应第三个事件。这样的话,在处理第三个事件时,这个事件已经是过期事件了,一旦处理必然出错。既然如此,关闭的这个连接的fd套接字置为-1能解决问题吗?答案时不能处理所有问题。
(3)特殊场景
假设3个事件对应的ngx_connection_t连接中的fd套接字原先是50,处理第一个事件时把这个连接的套接字关闭了,同时置为-1,并且调用ngx_free_connection将连接归还给连接池。在ngx_epoll_process_events方法的循环中开始处理第二个事件,恰好第二个事件时建立新连接事件,调用ngx_get_connection从连接池中取出的连接非常可能就是刚刚释放的第三个事件对应的连接。由于套接字50刚刚被释放,Linux内核非常可能把刚刚释放的套接字50又分给新建立的连接。因此,在循环第三个事件时,这个事件就是过期的了。它对应的事件是关闭的连接,而不是新建立的连接。
对应上面3个事件理解如下:
举个列子1,可以理解为第一个是写事件,第二个是新建事件,第三个是读时间。加入三个对应fd分别为50,20,50。instance在每个事件初始化的时候instance为1,只要ngx_connection不归还给连接池,随后的读写事件ptr里携带的instance和rev、wev的都能保持一致,instance都是1。
举个列子2,可以理解为第一个是写事件,第二个是新建事件,第三个是读时间。加入三个对应fd分别为50,20(socket_fd),50。现在第一个把连接归还给连接池了,恰好第二个取到刚释放的了,然后fd也给了50,但是每次get_connection是会把标志位置反的。所以就避免了过期事件的处理。
总结:
对于连接重复使用时,他的instance标志位一定是不同的。因此在ngx_epoll_process_events方法中一旦判断instance发生变化,就认为这是过期事件而不予处理。这种设计方法值得学习,几乎没有增加任何成本就能很好地解决了服务器开发时一定会出现的过期事件问题。
本文分享自 nginx遇上redis 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!