设置共享Socket 为了让``SO_REUSEPORT socket```选项起作用,应为HTTP或TCP(流模式)通信选项内的listen项直接引入新近的reuseport参数,就像下例这样: ? 引用reuseport参数后,对引用的socket,accept_mutex参数将会无效,因为互斥量(mutex)对reuseport来说是多余的。 如图所示,reuseport的每秒请求是其余的两到三倍,同时延迟和延迟标准差也是减少的。 ? 使用reuseport ,负载被均匀分离到了worker进程。 其他的基本的测试应该指出——当应用流量符合这种场景时 reuseport 也能大幅提高性能。
reuseport 简介 reuseport 是什么? reuseport 解决了什么问题? 设置当前worker是否开启监听端口复用(socket的SO_REUSEPORT选项)。 在有 reuseport 之前,一个端口号只能被一个 Socket 监听,有了 reuseport 之后,这个限制就被打破了:一个端口号可以被多个 Socket 同时监听。 workerman 如何利用 reuseport 虽然你只要在 workerman 中把 reusePort 设置为 true,就能享受到 Linux 的这个高级特性。 [2] 当 reusePort 为 true 时,情况就不同了。
前言:SO_REUSEPORT是提高服务器性能的一个特性,从Linux3.9后支持,本文从内核5.9.9的源码分析SO_REUSEPORT的实现,因为内核源码非常复杂,尽量把自己的思路说一下。 (sk));} reuseport_add_sock和reuseport_alloc的逻辑类似。 第一个socket执行listen时会执行reuseport_alloc,第二个socket执行listen时会执行reuseport_add_sock。 的sk_reuseport_cb字段 rcu_assign_pointer(sk->sk_reuseport_cb, reuse); return ret;} reuseport_alloc 后记:从内核实现的角度我们可以看到,SO_REUSEPORT的实现大概原理是内核会把每个进程的每个socket(设置了SO_REUSEPORT)维护起来。
前面,张戈博客在折腾 Nginx 的 SSL 优化时,注意到前人在 Nginx 的 listen 配置中,添加了 fastopen=3 reuseport 这 2 个参数。 于是脑补了下,原来是启用 Nginx 对 TCP_FASTOPEN 和 TCP_SO_REUSEPORT 新特性的支持,至于有什么好处,请自行脑补下-->传送门 ? 个参数时,报错了: nginx: [emerg] invalid parameter "fastopen=3" in *** 继续脑补了下,发现系统必须支持 TCP_FASTOPEN 和 TCP_SO_REUSEPORT ,然后重新编译 Nginx 加入如下参数: --with-cc-opt=-DTCP_FASTOPEN=23 TCP_SO_REUSEPORT 特性在 kernel-2.6.32-431.29.2 及 kernel 参数,比如: listen 80 fastopen=3 reuseport; 如果使用的是 Tengine,这个配置就会报错: nginx: [emerg] invalid parameter "reuseport
SO_REUSEPORT 许多人将SO_REUSEADDR当成了SO_REUSEPORT。 基本上来说,SO_REUSEPORT允许我们将任意数目的socket绑定到完全相同的源地址端口对上,只要所有之前绑定的socket都设置了SO_REUSEPORT选项。 SO_REUSEPORT选项。 SO_REUSEPORT并不等于SO_REUSEADDR。 Linux3.9加入了SO_REUSEPORT选项。
当第一个进程在启用了 SO_REUSEPORT 的套接字上调用 listen() 时,会分配它的 “struct sock” 结构中的指针- sk_reuseport_cb。 新套接字的 sk_reuseport_cb 指针指向第一个套接字的 sk_reuseport_cb 指针。这确保同一组的所有 LISTEN 套接字引用相同的 sk_reuseport_cb 指针。 */ return reuseport_alloc(new_sk); } 现在让我们了解 reuseport_select_sock() 如何选择 LISTEN 套接字。 来看如何实际使用 SO_REUSEPORT 选项 让我们通过两个测试来看看 SO_REUSEPORT 的影响 一个应用程序打开一个套接字用于监听,并创建两个进程。 Benchmarking SO_REUSEPORT 以下设置用于测量 SO_REUSEPORT 性能: 内核版本:4.17.13。
SO_REUSEPORT的意义是支持同用户下的多个进程同时监听一个IP和端口,本文介绍在Node.js中支持SO_REUSEPORT,以提升Node.js的性能。 目前,Node.js的TCP模块还没有支持SO_REUSEPORT。 这时候,SO_REUSEPORT出现了,SO_REUSEPORT更彻底地支持多个进程同时绑定同一个IP端口,架构如下。 ? 那么如何从SO_REUSEPORT特性获益呢? target=https%3A//www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ 3 SO_REUSEPORT的原理可以参考从内核看SO_REUSEPORT
当然可以,只要你使用 SO_REUSEPORT 这个参数。 由上可见,设置了SO_REUSEPORT参数之后,第二次listen中的bind操作是没用问题的,我们再看下对应的listen操作: // net/core/sock_reuseport.c int reuseport_add_sock 将sk2->sk_reuseport_cb字段值赋值给reuse。 2. 将sk放入到reuse->socks字段代表的数组中。 3. 将sk的sk_reuseport_cb字段也指向这个数组。 至此,有关SO_REUSEPORT参数的内容我们就讲完了。 SO_REUSEPORT参数是SO_REUSEADDR参数的超集,两个参数目的都是为了重复使用本地地址,但SO_REUSEADDR不允许处于listen状态的地址重复使用,而SO_REUSEPORT允许
#if defined(SO_REUSEPORT) && defined(__linux__) on = 1; if ((flags & UV_TCP_REUSEPORT) && setsockopt 首先导出一个新的常量 #if defined(SO_REUSEPORT) && defined(__linux__) NODE_DEFINE_CONSTANT(constants, UV_TCP_REUSEPORT 如果addressType是4或6说明是TCP协议,并且设置了UV_TCP_REUSEPORT(listen的时候传入),就会走到reuseport的逻辑,剩下的两个else是目前Node.js的逻辑。 我们看看ReusePort.js做了什么。 即使我们把reuseport改成false或者其他平台跑也没问题,效果如下 ? 我们看到在reuseport的情况下,负载还是挺均衡的。
SO_REUSEPORT 选项是什么 什么是惊群效应 SO_REUSEPORT 选项安全性相关的问题 Linux 内核实现端口选择过程的源码分析 SO_REUSEPORT 是什么 默认情况下,一个 IP 接下来我们来看 SO_REUSEPORT 底层实现原理, SO_REUSEPORT 源码分析 内核为处于 LISTEN 状态的 socket 分配了大小为 32 哈希桶。 = sk->sk_reuseport; // 如果 socket 启用了 SO_REUSEPORT 选项,通过源地址、源端口号、目标地址、目标端口号再次计算哈希值 if (reuseport = sk->sk_reuseport; // 有更合适的 reuseport 组,则根据 daddr、hnum、saddr、sport 再次计算哈希值 if (reuseport) { SO_REUSEPORT 在安全性方面的考虑主要是下面这两点。 1、只有第一个启动的进程启用了 SO_REUSEPORT 选项,后面启动的进程才可以绑定同一个端口。
现在介绍socket选项中如下几个可以提升服务端性能的选项: SO_REUSEADDR SO_REUSEPORT SO_ATTACH_REUSEPORT_CBPF/EBPF 验证环境:OS:centos 使用SO_REUSEPORT选项时可以不使用SO_REUSEADDR 选项。 设置方式为: setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); SO_ATTACH_REUSEPORT_CBPF/EBPF 其参数为bpf(2)返回的指向类型为BPF_PROG_TYPE_SOCKET_FILTER的程序的文件描述符 SO_ATTACH_REUSEPORT_CBPF:与SO_REUSEPORT 配合使用 ,用于将报文分给reuseport组(即配置了SO_REUSEPORT选项,且使用相同的本地地址接收报文 )中的socket。
这就是我们今天要说的 REUSEPORT 新特性。 本文中我们将阐述 REUSEPORT 是为了解决什么问题而产生的。如果有多个进程复用同一个端口,当用户请求到达时内核是如何选一个进程进行响应的。 二、REUSEPORT 的诞生 为了更高效地让多个用户态的进程接收和响应客户端的请求。Linux 在 2013 年的 3.9 版本中提供了 REUSEPORT 新特性。 我们来看下内核是如何支持 reuseport 这个特性的。 2.1 SO_REUSEPORT 设置 想给自己的服务开启 REUSEPORT 很简单,就是给自己 server 里 listen 用的 socket 上加这么一句。 我们来看看 bind 时对 reuseport 的处理过程。
(3)SO_REUSEPORT 特性(现代内核) 内核级负载均衡:Linux 3.9+ 引入 SO_REUSEPORT 特性,允许多个进程绑定同一端口,内核自动将新连接分配给不同的进程,无需用户态锁竞争 Nginx 配置: events { use epoll; } http { listen 80 reuseport; accept_mutex off; } 启用 reuseport (3)SO_REUSEPORT 特性(现代内核) 替代 accept_mutex:启用 reuseport 后,内核直接分配新连接到不同进程,无需用户态锁竞争,彻底避免 epoll 惊群。 内核特性 性能影响 传统锁机制增加用户态开销,reuseport 可显著优化 reuseport 完全消除用户态锁竞争,性能更优 适用场景 低版本内核或需兼容旧环境时使用 accept_mutex;高版本内核推荐 reuseport 同上,reuseport 为首选方案 五、结论 Nginx 通过共享锁机制(accept_mutex)和现代内核特性(如 SO_REUSEPORT)有效解决了 accept 惊群和
reuseport || !sk2->sk_reuseport || (sk2->sk_state != TCP_TIME_WAIT && ! SO_REUSEPORT SO_REUSEPORT是Linux在3.9版本引入的新功能。 鉴于此,Linux增加了SO_REUSEPORT,而之前bind中判断是否冲突的下面代码也是为这个参数而添加的逻辑: if(!reuseport || ! if (score > hiscore) { result = sk; hiscore = score; reuseport = sk->sk_reuseport; if (reuseport Nginx已经采用SO_REUSEPORT Nginx在1.9.1版本的时候引入了SO_REUSEPORT,配置如下: http { server { listen 80
, err error) { l = &listener{network: network, addr: addr, reusePort: reusePort} err = l.normalize () return } // 归一化,选择不同的协议,进行初始化socket // socket // noblocking // bind // reusePort // listen func (ln.network, ln.addr, ln.reusePort) ln.network = "tcp" case "udp", "udp4", "udp6": ln.fd, ln.lnaddr, err = reuseport.UDPSocket(ln.network, ln.addr, ln.reusePort) ln.network = " (ln.network, ln.addr, ln.reusePort) default: err = errors.ErrUnsupportedProtocol }
1 : 0; int ret = ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval , static_cast<socklen_t>(sizeof optval)); if (ret < 0 && on){ LOG_FATAL("SO_REUSEPORT failed int sockfd, const InetAddress &)>; Accept(EventLoop *loop, const InetAddress &listenAddr, bool reuseport FUNCTION__,__LINE__,errno); } } Accept::Accept(EventLoop *loop, const InetAddress &listenAddr, bool reuseport , O_RDONLY | O_CLOEXEC)) { acceptSocket_.setReuseAddr(true); acceptSocket_.setReusePort(reuseport
80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; listen 443 quic reuseport ; # 配置 H3 协议守护,注意reuseport放在默认虚机 listen [::]:443 quic reuseport; server_name xx.xxx.com
但是先fork在createsocket,端口被占用了,那能不能设置REUSEPORT。 然后尝试设置REUSEPORT参数,结果也是不尽人意,编译出错。 ? 原来,REUSEPORT是只有在3.9以上的内核版本才支持,我的开发机是2.6,应该不支持这次编译。 但是具体原因是为啥,机器的操作系统是不支持SO_REUSEPORT的,问下了操作系统的同事,给到的答复是目前的操作系统是打了上游的patch。 再细问一下,标准库的头文件也没有SO_REUSEPORT的定义。给到的答复是头文件和内核不同步。好吧,其实我很不愿意接受了这个答复。 写在最后 多进程情况下,都建议使用REUSEPORT,就不会出现那么多不稳定的问题。
最终导致socket锁竞争 避免锁竞争 使用SO_REUSEPORT选项分割sockets 该选项在内核3.9引入,默认使用流(报文首部)哈希来选择socket ? 使用SO_REUSEPORT sar -u ALL -P ALL 1 ? 此时软中断消耗的CPU就比较合理了,从下面火焰图可以看到中断处理消耗的CPU缩短了 ? SO_REUSEPORT默认使用流哈希来选择队列,不同的CPU核可能会选择相同的sockets,导致竞争。 ? 避免socket锁竞争 根据CPU核号选择socket 通过SO_ATTACH_REUSEPORT_CBPF/EBPF实现 在内核4.5引入上述功能 ? 此时软中断之间不再产生竞争 用法可以参见内核源码树中的例子:tools /testing/selftests /net /reuseport_bpf_cpu.c 启用SO_ATTACH_REUSEPORT_EPBF
实现reuseport时为了充分利用多核,提升连接建立的效率。 如果启用reuseport,让新进程可以直接监听同一个地址,这会在新进程里创建一个新的套接字。 reuseport不能实现平滑重启,但是能提升建连效率。reuseport和“fork共享套接字”是互补的关系。 nginx在1.9.1版本后也添加了reuseport支持,实现上是直接在master里监听worker数量对应的reuseport套接字,再让每个worker进程继承从中继承一个套接字。 实现细节请参考nginx源码分析—reuseport的使用。 3. nginx源码分析—reuseport的使用