SO_REUSEPORT 端口复用socket选项

    Liunux 3.9版本对于TCP和UDP增加了端口复用SO_REUSEPORT的socket选项,这个选项允许相同主机上的多个socket绑定到同一个端口,用来提升多核系统上多线程网络server应用的性能。

    SO_REUSEPORT的基本概念非常简单,多个server(进程或线程)能绑定到相同的端口上,如如下代码:

    int sfd = socket(domain, socktype, 0);

    int optval = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

    bind(sfd, (struct sockaddr *) &addr, addrlen);

   所以只要第一个server在bind之前设置了这个选项,那么其他所有的server在设置了这个选项后,也能绑定到同一个端口。第一个端口必须设置SO_REUSEPORT的要求可以防止端口劫持——有可能流氓应用试图绑定到一个已经运行的server的端口上以劫持相关数据。为阻止不相干的进程试图使用SO_REUSEPORT选项劫持端口报文,第一个server在bind后生成一个user ID,所有的后续绑定到这个端口的server必须有与第一个server相匹配的user ID。

     SO_REUSEPORT对TCP和UDP的socket都生效。对 TCP socket,它允许多个listen socket(通常每个在不同的线程)绑定到相同的端口,每个线程可以通过调用accept()函数来接受client的连接。这提供了一种传统多线程server处理单一的socket外的另一个可选方法。

    在SO_REUSEPORT前,传统处理有两种模型:

    模型一:一个listen线程用来accept所有的新建连接,然后将连接转发给其他的进程或线程处理。这个模型存在的问题是listen线程会成为性能瓶颈。在早期对SO_REUSEPORT的讨论中,作者Tom提出他的应用需要处理每秒40000个新建连接,如果了解到Tom在Google工作,对这个数字一点也不会感到惊讶。

  模型二:另外一个处理方法是每个server都执行accept()函数,如如下代码:

    while (1) {
        new_fd = accept(...);
        process_connection(new_fd);
    }

  这个模型的问题,就像Tom指出的,是多个线程等待accept()调用时,唤醒调度并不公平,所以,在高负载情况下,不同处理线程的负载会非常不平衡。在Google,处理连接最多的线程和最少的线程间有3倍的差别,这导致多核CPU特性没有最大发挥出来。作为对比,SO_REUSEPORT基本能实现所有bind相同端口的server间的处理负载平衡。

    与TCP类似,SO_REUSEPORT允许多个UDP socket绑定到相同的端口。这个特性对于一些应用特别有用,比如处理DP报文的DNS服务器。利用SO_REUSEPORT,每个线程可以直接调用recv()用来接收来自端口的报文。传统的方法是所有的线程通过共享的socket来执行recv()调用,和上面提到的TCP模型二类似,这也会导致线程负载的不平衡,作为对比,SO_REUSEPORT会将UDP报文平滑分发给接收线程。

    Tom提到传统的SO_REUSEADDR选项已经支持多个UDP socket绑定到相同的端口,并能接收报文,但是和SO_REUSEPORT对比,SO_REUSEADDR不能阻止端口劫持,也不能平滑分发报文到接收线程。

    Tom提交的patch还有两个有价值的关注点:

    第一,这个特性的有用方面,连接和数据按照4元组(远端IP和端口、本地IP和端口)来哈希,然后分发给不同的server。这就意味着,如果一个client使用相同的socket向server发送了一系列的报文,那么这些报文将会被直接分发给同一个接收server,这极大简化了server和client间的会话状态处理。

    第二,目前针对TCP的SO_REUSEPORT选项也存在缺陷,如果绑定到端口的socket数目发生变化,比如server启动或server退出,可能导致进入的链接在三次握手过程中被丢弃。问题的原因是,新建连接请求的SYN握手报文被绑定到特定的server,如果绑定端口server的个数发生变化,那么SO_REUSEPORT逻辑可能不会讲三次握手最后的ACK报文分配给正确的socket,在这种情况下,client的连接会被重置,同时server会残留一个SYN请求状态。这个问题仍在解决中,解决方法是维护一个多个server间共享的连接请求表。

    SO_REUSEPORT选项并不是标准的socket选项,但是在一系列类UNIX系统上(如BSD)均被支持。它提供了一种在多核系统上实现最佳性能的网络应用框架,因此会在更多追求高性能的应用中得到使用。

  --

   译者注:SO_REUSEPORT在monkey-server上的使用,欢迎大家参考关注 https://github.com/monkey/monkey/commit/d1da249a0b5e8f5765ea8031919fb32e93c57cb8

 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值