NGINX Socket Sharding

NGINX版本1.9.1引入了一项新功能-SO_REUSEPORT,SO_REUSEPORT是针对socket处理的选项。让多个处理单元监听来自相同IP地址和端口的连接。

该选项适用于许多较新版本的操作系统,包括DragonFly BSD和Linux(内核版本3.9及更高版本)。SO_REUSEPORT选项让多个socket监听同一个IP地址和端口组合。然后操作系统内核(kernel)以socket负载均衡的方式处理传入的连接。

SO_REUSEPORT还有一些其他的作用,可以轻松实现滚动升级Nginx(NGINX已经通过不同方式支持滚动升级)。对于NGINX,启用SO_REUSEPORT选项可以减少锁争用,从而提高某些场景下的性能。

正常情况下NGINX处理用户请求的示意图如下:

NGINX Socket Sharding

当请求到达时,单个socket监听会去通知所有的工作进程,而每个工作进程都尝试进行连接。

启用SO_REUSEPORT该选项后,每个IP地址和端口组合(一个用户的连接)有多个socket监听器,每个工作进程一个。

NGINX Socket Sharding

内核(kernel)决定哪个socket侦听器(以及暗示哪个worker)可以获得连接。这样可以减少因接受新连接,而导致的工作进程之间的锁竞争,从而提高多核系统的性能。

但是,这也意味着当一个工作进程因阻塞操作而停顿时,不仅会影响该工作进程已经接受的连接,还会影响后续内核分配给该工作进程的连接请求。

配置Socket Sharding

启用SO_REUSEPORT选项,只需要将reuseport参数包含在HTTP或TCP的(流模块)的listen指令中,如下例所示:

http {
     server {
          listen 80 reuseport;
          server_name  localhost;
          # ...
     }
}

stream {
     server {
          listen 12345 reuseport;
          # ...
     }
}

启用reuseport参数会禁用socket accept_mutex指令(accept_mutex会使工作进程依次接受新连接,相反,将通知所有工作进程,如果新连接的数量很少,则会导致系统资源的浪费。),因为在启用了reuseport后mutex是多余的。如果nginx的其他端口没有设置reuseport,accept_mutex仍然是可以设置的。

reuseport基准测试结果

在一个36-core的AWS实例运行了4个nginx工作进程,使用http基准测试工具WRK,让NGINX返回字符串OK而非文件,为了消除网络延迟的影响,以localhost的方式运行了客户端和NGINX,比较了三种NGINX配置:默认(相当于accept_mutex on),with accept_mutex off和with reuseport。

如图所示,reuseport每秒请求增加2到3倍,并降低了客户端和服务器之间的交互延迟。

socket sharding nginx

把客户端和NGINX运行在各自的主机上也作了相关的基准测试,并且NGINX返回了一个HTML文件。如下表所示,使用reuseport,延迟的减少与之前的基准相似。其他结果(表中未显示)也令人振奋。使用reuseport时,负载均匀分布在工作进程中。

在默认条件下(相当于accept_mutex on),只有部分工作进程的负载比较高,而accept_mutex off所有工作进程都经历了高负荷。

  Latency (ms) Latency stdev (ms) CPU Load
Default 15.65 26.59 0.3
accept_mutex off 15.59 26.48 10
reuseport 12.35 3.15 0.3

在这些基准测试中,连接请求的速率很高,但请求不需要大规模的处理。其他初步测试还表明,当流量与profile匹配时,reuseport性能最高(例如,listen reuseport参数在mail上下文中不适用,因为电子邮件流量肯定与这个profile不匹配),建议进行测试,以确定reuseport是否可以提高NGINX部署的性能。