Linux-SO_REUSEPORT特性

Linux对于多核处理网络问题的时候,一般是俩种模型

1.单进程或者单线程创建socket,同时监听链接socket和监听socket,有事物发生的话就创建进程或者线程去处理

这种的弊端在于单线程或者单进程会发生性能瓶颈

2.单进程或者单线程创建socket,并进行listen,然后创建一批进程或者线程去同时accept该套接字

这种的弊端在于可能会发生惊群现象,就是事件发生唤醒多个进程但是只有一个进程可以accept成功,引起不必要的开销。解决这种问题用的就是锁,锁的话其实也是很大的开销,而且每个线程处理的也不是很均

很难做到CPU之间的负载均衡

SO_REUSEPORT支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,解决的问题:

  • 允许多个套接字 bind()/listen() 同一个TCP/UDP端口,注意是在同一个用户下的
    • 每一个线程拥有自己的服务器套接字
    • 在服务器套接字上没有了锁的竞争
  • 内核层面实现负载均衡
  • 安全层面,监听同一个端口的套接字只能位于同一个用户下面

其核心的实现主要有三点:

  • 扩展 socket option,增加 SO_REUSEPORT 选项,用来设置 reuseport。
  • 修改 bind 系统调用实现,以便支持可以绑定到相同的 IP 和端口
  • 修改处理新建连接的实现,查找 listener 的时候,能够支持在监听相同 IP 和端口的多个 sock 之间均衡选择。

有了SO_RESUEPORT后,每个进程可以自己创建socket、bind、listen、accept相同的地址和端口,各自是独立平等的。让多进程监听同一个端口,各个进程中accept socket fd不一样,有新连接建立时,内核只会唤醒一个进程来accept,并且保证唤醒的均衡性。

并且可以发现多个进程或者多个线程可以监听同一个端口

除此之外,对于设置了SO_REUSEPORT选项的socket,Linux kernel还会执行一些别的系统所没有的特别的操作:对于绑定于同一地址端口组合上的UDP socket,kernel尝试在它们之间平均分配收到的数据包;对于绑定于同一地址端口组合上的TCP监听socket,kernel尝试在它们之间平均分配收到的连接请求(调用accept()方法所得到的请求)。这意味着相比于其他允许地址复用但随机将收到的数据包或者连接请求分配给连接在同一地址端口组合上的socket的系统而言,Linux尝试了进行流量分配上的优化。比如一个简单的服务器进程的几个不同实例可以方便地使用SO_REUSEPORT来实现一个简单的负载均衡,而且这个负载均衡有kernel负责, 对程序来说完全免费!

对于大多数udp服务程序来说,一般采取的策略就是,主线程调用socket绑定socket到地址,然后应用会开启多个线程同时去调用recvmsg函数,每个线程会去竞争的调用recvmsg,导致线程的负载会不一样;但是有了这个标志的话,相同的地址加端口可以被不同的socket重复的绑定,这样的多线程就可以监听不同的socket了但是他们监听的其实都是同一个地址,那么在内核有数据到来的时候就会把请求均衡的分给不同的socket,也就是不同的线程了,这样内核可以自动的帮你做到负载均衡

你可能感兴趣的:(powerdns源码)