nginx 优秀之处在于它的事件处理机制,其业务核心是围绕事件来展开的。worker进程受事件驱动,当有事件发生时处理事件。
事件分为定时器事件和网络事件。处理事件时,采用如select和epoll之类的机制,既保证能监听到网络事件,又保证不会无限期阻塞,无法处理定时器事件,在等待网络事件的同时,也设定超期时间,巧妙之处超期时间则是所有定时器事件中最短定时的时间,这样就保证了定时器事件能及时处理。
也可以这样认为,worker进程在处理定时器事件的间隙,处理网络事件,没准儿网络事件在它看来只是副业。
网络事件主要三种,accept事件,read事件,write事件。
accept事件是个特殊事件,是在监听端口产生的事件,nginx 有多个worker进程,每个进程都监听所有的端口。那么问题来了,accept事件来了,哪个worker处理。
和大多数的出来一样,临界资源靠锁来保护,需要访问资源,就要竞争锁。这就涉及到另外两个问题,公平问题 和 锁的快速释放。
公平问题,不能有的worker已经满负荷运载,导致无法及时处理事件而致事件事件失效,其它worker则空闲,这将导致nginx的能力极大的下降。这个时候解决方法是worker要设置自己的阀值,当超过这个阀值的时候,就不去竞争锁,worker有个变量nginx_accept_mutex_disable,每次处理完accept事件后,就更新它的值为 当前链接数/8 - 剩余的空闲链接(这个概念后续再讲), 如果nginx_accept_mutex_disable 大于0 则不再竞争锁。那么当所有的worker都处于比较忙的时候怎么办呢,nginx_accept_mutex_disable 确实是大于0,但是worker仍然有空闲链接,难道不用了么。当然不会资源浪费,当nginx_accept_mutex_disable 大于0时,nginx_accept_mutex_disable 每次处理完其它事件的时候都会减1。总结一下是worker进程负载较多时,就减少竞争锁的机会,当所有worker进程负载较多时候,accpet事件的处理机会就减少,非accept事件就加紧处理,这样就可以释放出更多资源来处理accept事件了。
锁的快速释放,当竞争到accept锁后,锁尽可能快速释放,才能保证其它worker使用临界资源,这样accept事件会被快速处理,不然让accept事件成为瓶颈。这个时候的网络事件中的accept事件会被优先处理,然后释放锁,之后才处理非accpet事件。
总结,定时器事件和网络事件的分类让nginx 能轻松及时的处理这两种事件,accept事件锁机制能够让worker之间高效的出来网络事件。总之nginx 的事件驱动以及这些巧妙之处让nginx在处理网络并发连接时具备了极大的优势。
nginx 源码笔记 --事件
最新推荐文章于 2022-07-16 16:26:23 发布