首先看下 连接池的获取以及释放

ngx_connection_t *
ngx_get_connection(ngx_socket_t s, ngx_log_t *log) //从连接池中获取一个ngx_connection_t
{
ngx_uint_t instance;
ngx_event_t *rev, *wev;
------------------------------------------------ instance = rev->instance; ngx_memzero(rev, sizeof(ngx_event_t));
ngx_memzero(wev, sizeof(ngx_event_t));
  1. / 新的event中的instance在原来的基础上取反。意思是,该event被重用了。因为在请求处理完
  2. // 之前,instance都不会被改动,而且当前的instance也会放到epoll的附加信息中,即请求中event
  3. // 中的instance跟从epoll里得到的instance是相同的,不同则是异常了,需要处理
    rev->instance = !instance;
wev->instance = !instance; rev->index = NGX_INVALID_INDEX;
wev->index = NGX_INVALID_INDEX; rev->data = c;
wev->data = c; return c;
}

void
ngx_free_connection(ngx_connection_t *c) //归还c到连接池中
{
c->data = ngx_cycle->free_connections;
ngx_cycle->free_connections = c;
ngx_cycle->free_connection_n++; -------------
}
 

  新的event中的instance在原来的基础上取反。意思是,该event被重用了。因为在请求处理之前,instance都不会被改动,而且当前的instance也会放到epoll的附加信息中,即请求中event 中的instance跟从epoll里得到的instance是相同的,不同则是异常了,需要处理。

 //遍历本次epoll_wait返回的所有事件
for (i = 0; i < events; i++) { //和ngx_epoll_add_event配合使用
/*
对照着上面提到的ngx_epoll_add_event方法,可以看到ptr成员就是ngx_connection_t连接的地址,但最后1位有特殊含义,需要把它屏蔽掉
*/
c = event_list[i].data.ptr; //通过这个确定是那个连接 instance = (uintptr_t) c & 1; //将地址的最后一位取出来,用instance变量标识, 见ngx_epoll_add_event /*
无论是32位还是64位机器,其地址的最后1位肯定是0,可以用下面这行语句把ngx_connection_t的地址还原到真正的地址值
*/ //注意这里的c有可能是accept前的c,用于检测是否客户端发起tcp连接事件,accept返回成功后会重新创建一个ngx_connection_t,用来读写客户端的数据
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); rev = c->read; //取出读事件 //注意这里的c有可能是accept前的c,用于检测是否客户端发起tcp连接事件,accept返回成功后会重新创建一个ngx_connection_t,用来读写客户端的数据 if (c->fd == -1 || rev->instance != instance) { //判断这个读事件是否为过期事件
//当fd套接字描述符为-l或者instance标志位不相等时,表示这个事件已经过期了,不用处理
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}

fd在当前处理时变成-1,意味着在之前的事件处理时,把当前请求关闭了,即close fd并且当前事件对应的连接已被还回连接池,此时该次事件就不应该处理了

其次,如果fd > 0,那么是否本次事件就可以正常处理,就可以认为是一个合法的呢?答案是NO       

这里我们给出一个情景:当前的事件序列是: A ... B ... C ... 其中A,B,C是本次epoll上报的其中一些事件,但是他们此时却相互牵扯: A事件是向客户端写的事件,B事件是新连接到来,C事件是A事件中请求建立的upstream连接,此时需要读源数据,然后A事件处理时,由于种种原因将C中upstream的连接关闭了(比如客户端关闭,此时需要同时关闭掉取源连接),自然C事件中请求对应的连接也被还到连接池(注意,客户端连接与upstream连接使用同一连接池), 而B事件中的请求到来,获取连接池时,刚好拿到了之前C中upstream还回来的连接结构,当前需要处理C事件的时候,  c->fd != -1,因为该连接被B事件拿去接收请求了,而rev->instance在B使用时,已经将其值取反了,所以此时C事件epoll中携带的instance就不等于rev->instance了,因此我们也就识别出该stale event,跳过不处理了。

//ngx_epoll_add_event表示添加某种类型的(读或者写,使用LT促发方式)事件,ngx_epoll_add_connection (读写一起添加上去, 使用EPOLLET边沿触发方式)
static ngx_int_t
ngx_epoll_add_connection(ngx_connection_t *c) //该函数封装为ngx_add_conn的,使用的时候为ngx_add_conn
{
struct epoll_event ee; ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP; //注意这里是水平触发
ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance); --------------------------------------
c->read->active = 1;
c->write->active = 1; return NGX_OK;
} static ngx_int_t //通过flag指定促发方式,NGX_CLEAR_EVENT为ET方式,NGX_LEVEL_EVENT为LT方式
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) //该函数封装为ngx_add_event的,使用的时候为ngx_add_event
{ //一般网络事件中的报文读写通过ngx_handle_read_event ngx_handle_write_event添加事件
int op;
uint32_t events, prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee; c = ev->data; //每个事件的data成员都存放着其对应的ngx_connection_t连接
----------------------------- ee.events = events | (uint32_t) flags; //加入flags参数到events标志位中
/* ptr成员存储的是ngx_connection_t连接,可参见epoll的使用方式。*/
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
-
-------------------------------------------------
//第一次添加epoll_ctl为EPOLL_CTL_ADD,如果再次添加发现active为1,则epoll_ctl为EPOLL_CTL_MOD
ev->active = 1; //将事件的active标志位置为1,表示当前事件是活跃的 ngx_epoll_del_event中置0
#if 0
ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
#endif return NGX_OK;
}

Q:  A ... B ...  B' ... C,其中A,B,C跟之前讨论的情形一样,我们现在很明确,B中获得A中释放的连接时,会将instance取反,这样在C中处理时,就可以发现rev->instance != instance,从而发现“stale event”。那么我们假设B中处理时,又将该connection释放,在B'中再次获得,同样经过instance取反,这时我们会发现,instance经过两次取反时,就跟原来一样了,这就不能通过fd == -1与rev->instance != instance的验证????????????????????????????

  新连接通过accept来获得,即函数ngx_event_accept。在这个函数中会ngx_get_connection,从而拿到一个连接,然后紧接着初始化这个连接,即调用ngx_http_init_connection,在这个函数中通常是会此次事件挂到post event链上去:

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

ngx instance的更多相关文章

  1. ngx.ctx

    https://github.com/openresty/lua-nginx-module#ngxctx 要点 生命周期和请求一致 每个请求的ngx.ctx是相互独立的,包括ngx.location. ...

  2. nginx&http 第三章 ngx http 框架处理流程

    1. nginx 连接结构 ngx_connection_t 这个连接表示是客户端主动发起的.Nginx服务器被动接受的TCP连接,我们可以简单称其为被动连接.同时,在有些请求的处理过程中,Nginx ...

  3. nginx&http 第三章 ngx 事件event epoll 处理

    1. epoll模块命令集 ngx_epoll_commands  epoll模块上下文 ngx_epoll_module_ctx  epoll模块配置 ngx_epoll_module static ...

  4. nginx&http 第二章 ngx 事件event处理 数据结构

    ngx_event.c :这个文件主要放置Nginx事件event模块的核心代码. 包含:进程事件分发器(ngx_process_events_and_timers).事件模块的模块和配置.模块初始化 ...

  5. 再部署一个 instance 和 Local Network - 每天5分钟玩转 OpenStack(131)

    上一节部署了 cirros-vm1 到 first_local_net,今天我们将再部署 cirros-vm2 到同一网络,并创建 second_local_net. 连接第二个 instance 到 ...

  6. 将 instance 部署到 OVS Local Network - 每天5分钟玩转 OpenStack(130)

    上一节创建了 OVS 本地网络 first_local_net,今天我们会部署一个 instance 到该网络并分析网络结构.launch 一个 instance,选择 first_local_net ...

  7. java.lang.NoSuchFieldError: org.apache.http.message.BasicLineFormatter.INSTANCE

    Android发出HTTP请求时出现了这个错误: java.lang.NoSuchFieldError: org.apache.http.message.BasicLineFormatter.INST ...

  8. 严重: Exception sending context initialized event to listener instance of class

    问题描述:Exception sending context initialized event to listener instance of class org.springframework.w ...

  9. 将 instance 连接到 flat_net - 每天5分钟玩转 OpenStack(88)

    上一节我们创建了 "flat_net",本节将在此网络中部署 instance 并验证连通性. launch 新的 instance “cirros-vm1”,选择网络 falt_ ...

随机推荐

  1. 正式班D9

    2020.10.16星期五 正式班D9 一.vmware workstation的使用 虚拟机管理软件 定义 虚拟机(Virtual Machine)软件是一套特殊的软件,它可以作为操作系统独立运行, ...

  2. k8s node上查看节点

    node执行 mkdir -p /root/.kube master执行 scp admin.conf node1:/root/.kube/config

  3. spring boot:给接口增加签名验证(spring boot 2.3.1)

    一,为什么要给接口做签名验证? 1,app客户端在与服务端通信时,通常都是以接口的形式实现, 这种形式的安全方面有可能出现以下问题: 被非法访问(例如:发短信的接口通常会被利用来垃圾短信) 被重复访问 ...

  4. python 保存登录状态 cookie

    import requests from lxml import etree import faker url = "https://www.yeves.cn/admin/Articles& ...

  5. 【应用服务 App Service】如何移除App Service Response Header中包含的服务器敏感信息

    问题描述 有些情况下,当应用部署到App Service上后,在有些Response Header中,可以看见关于服务器的一些信息,这样会导致隐藏的安全问题,所以可以在web.config中移除某些关 ...

  6. Java爬取同花顺股票数据(附源码)

    最近有小伙伴问我能不能抓取同花顺的数据,最近股票行情还不错,想把数据抓下来自己分析分析.我大A股,大家都知道的,一个概念火了,相应的股票就都大涨. 如果能及时获取股票涨跌信息,那就能在刚开始火起来的时 ...

  7. pip安装与使用

    介绍 pip是python包管理工具,提供了对python包的查找,下载,安装,卸载功能. 安装 检查是否安装 pip --version 安装 curl https://bootstrap.pypa ...

  8. A. Cubes Sorting 解析(思維)

    Codeforce 1420 A. Cubes Sorting 解析(思維) 今天我們來看看CF1420 題目連結 題目 給一個數列\(a\),求能不能在不超過\(\frac{n(n-1)}{2}-1 ...

  9. iOS 14 egret 游戏卡顿问题分析和部分解决办法

    现象 总体而言,iOS 14 渲染性能变差,可以从以下三个测试看出. 测试1:简单demo,使用egret引擎显示3000个图(都是同一个100*100 png 纹理),逐帧做旋转.(博客园视频播放可 ...

  10. 微信小程序--基于ColorUI构建皮皮虾短视频去水印组件(仅供学习使用)

    微信小程序--基于ColorUI构建皮皮虾短视频去水印组件(仅供学习使用) 没错,我是皮友,我想学习舞蹈(/doge)和瑜伽 ,要无水印的那种有助于我加深学习. 1.组件效果展示 2.组件引入准备 h ...