nginx so_reuseport
nginx官网上的描述:
Socket Sharding in NGINX Release 1.9.1
知乎上的一个网友的通俗解释:
作者:凡柯
链接:https://www.zhihu.com/question/51618274/answer/126729306
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我来回答一下自己的问题,抛砖引玉吧先描述下Nginx的网络模型
1 nginx的master进程创建一系列的监听套接字(比如需要监听不同的端口,80,443等),下面都以监听80端口为例来说明。
2 fork出多个worker进程,这些worker进程继承了监听套接字。
3 而这种多进程监听同一端口的模型会有惊群现象(先不讨论内核是否解决了惊群,nginx作为一个跨平台的server,从自身解决了惊群),即监听套接字上有请求到来时,内核会唤醒所有的进程。
4 在linux平台下采用epoll网络事件驱动,每个worker创建自己的epoll。
5 Nginx的惊群解决手段: 如果有多个worker进程,且开启了accept_mutux
锁(默认开启)这时候每个worker不会将监听套接字加入到自己的epoll中,而是会去抢一把自旋锁,即对监听套接字“权力”,抢到的worker进程会将监听套接字加入自己的epoll中,accept新请求,然后释放锁。所以,如果没有这种强锁机制,每个worker的epoll中都会监视监听套接字,这样每次请求到来时,每个worker都会被唤醒,而最终accept这个请求的只能有一个,其它的worker的唤醒是浪费的。应该说在低并发情况下,这种处理机制会很好地提升cpu效率。现在很多实例证明:在并发很高的情况下,nginx这种处理惊群的机制会导致处理效率的下降,所以现在很多建议是关闭accept_mutux锁,这样每个worker中的epoll中都会监视监听套接字。幸运的是,从nginx的高版本开始,这个锁默认是已经关闭的了。为什么在高并发的情况下,关闭锁导致使惊群现象产生,也会提升性能呢?网上有个很形象的例子,这里我借用下:试想,有一群鸡(是真正的鸡),你撒谷粒给这群鸡吃。
a,一粒粒撒的时候,如果不加处理,每个鸡都会跳起来,但最终只有一只鸡能够吃到这粒米,所以在一粒粒撒的时候,需要有锁,不能让每个鸡都跳起来,这样浪费它们的精力,必须要让它们遵守秩序,一个个来(加锁)
b,然而,如果你撒了一大把谷粒,这时候还让它们一个个来,岂不是很不合理,所以,在撒大把谷粒的情况下,这些鸡全部跳起来抢食才是科学的,这样才能更加快速地消耗掉这些谷粒。(不加锁)。 上面的例子虽然有些粗糙,但是很形象。
6 回到我们的话题SO_REUSEPORT这个选项,官方在nginx.19.1中支持这个,且经过测试,开启这个选项,会使得的nginx的性能提升3倍。具体测试可见:Socket Sharding in NGINX OSS Release 1.9.1 他们比较了三种情况,常规情况(开启锁),关闭锁,和开启SO_REUSEPORT。 对于关闭锁比开启锁提升性能,这个好理解。但SO_REUSEPORT这个。就是我的疑问了。
7 我的疑问是这样的,通过master
fork出多个worker,这些worker都是共同监听了同一个端口,那么reuse_port这个,根据man手册描述,是让多个套接字共同监听同一个端口。但是master对于一个端口只创建了一个套接字,哪里来的多个套接字呢?从那个测试的图来看,好像在文件系统中对同一个端口关联了多个套接字。。。这科学么?8 如果内核真如测试的图中描述的那样,对于每个worker都有自己的监听套接字,且这些监听套接字都bind同一个端口,这样,当请求来时,内核会负责将这些请求均匀分配到不同的监听套接字,这样看来
确实会提升性能。但问题的关键是,对于同一个端口来说,这些不同的套接字是哪里来的呢??