Linux 最新SO_REUSEPORT特性

1、前言

  昨天总结了一下Linux下网络编程“惊群”现象,给出Nginx处理惊群的方法,使用互斥锁。为例发挥多核的优势,目前常见的网络编程模型就是多进程或多线程,根据accpet的位置,分为如下场景:

  (1)单进程或线程创建socket,并进行listen和accept,接收到连接后创建进程和线程处理连接

  (2)单进程或线程创建socket,并进行listen,预先创建好多个工作进程或线程accept()在同一个服务器套接字、

                      

这两种模型解充分发挥了多核CPU的优势,虽然可以做到线程和CPU核绑定,但都会存在:

  • 单一listener工作进程胡线程在高速的连接接入处理时会成为瓶颈
  • 多个线程之间竞争获取服务套接字
  • 缓存行跳跃
  • 很难做到CPU之间的负载均衡
  • 随着核数的扩展,性能并没有随着提升

参考:http://www.blogjava.net/yongboy/archive/2015/02/12/422893.html

Linux kernel 3.9带来了SO_REUSEPORT特性,可以解决以上大部分问题。

2、SO_REUSEPORT解决了什么问题

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,并且保证唤醒的均衡性。

3、测试代码

 1 include <stdio.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <sys/socket.h>
 5 #include <netinet/in.h>
 6 #include <arpa/inet.h>
 7 #include <assert.h>
 8 #include <sys/wait.h>
 9 #include <string.h>
10 #include <errno.h>
11 #include <stdlib.h>
12 #include <fcntl.h>
13
14 #define IP   "127.0.0.1"
15 #define PORT  8888
16 #define WORKER 4
17 #define MAXLINE   4096
18
19 int worker(int i)
20 {
21     struct sockaddr_in address;
22     bzero(&address, sizeof(address));
23     address.sin_family = AF_INET;
24     inet_pton( AF_INET, IP, &address.sin_addr);
25     address.sin_port = htons(PORT);
26
27     int listenfd = socket(PF_INET, SOCK_STREAM, 0);
28     assert(listenfd >= 0);
29
30     int val =1;
31     /*set SO_REUSEPORT*/
32     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))<0) {
33         perror("setsockopt()");
34     }
35     int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
36     assert(ret != -1);
37
38     ret = listen(listenfd, 5);
39     assert(ret != -1);
40     while (1) {
41         printf("I am worker %d, begin to accept connection.\n", i);
42         struct sockaddr_in client_addr;
43         socklen_t client_addrlen = sizeof( client_addr );
44         int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
45         if (connfd != -1) {
46             printf("worker %d accept a connection success. ip:%s, prot:%d\n", i, inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
47         } else {
48             printf("worker %d accept a connection failed,error:%s", i, strerror(errno));
49         }
50         char buffer[MAXLINE];
51         int nbytes = read(connfd, buffer, MAXLINE);
52         printf("read from client is:%s\n", buffer);
53         write(connfd, buffer, nbytes);
54         close(connfd);
55     }
56     return 0;
57 }
58
59 int main()
60 {
61     int i = 0;
62     for (i = 0; i < WORKER; i++) {
63         printf("Create worker %d\n", i);
64         pid_t pid = fork();
65         /*child  process */
66         if (pid == 0) {
67             worker(i);
68         }
69         if (pid < 0) {
70             printf("fork error");
71         }
72     }
73     /*wait child process*/
74     while (wait(NULL) != 0)
75         ;
76     if (errno == ECHILD) {
77         fprintf(stderr, "wait error:%s\n", strerror(errno));
78     }
79     return 0;
80 }

我的测试机器内核版本为:

测试结果如下所示:

从结果可以看出,四个进程监听相同的IP和port。

4、参考资料

http://lists.dragonflybsd.org/pipermail/users/2013-July/053632.html

http://www.blogjava.net/yongboy/archive/2015/02/12/422893.html

http://m.blog.chinaunix.net/uid-10167808-id-3807060.html

时间: 2024-10-10 21:40:03

Linux 最新SO_REUSEPORT特性的相关文章

[转]linux最新分区方案

FROM : http://www.cnblogs.com/chenlulouis/archive/2009/08/27/1554983.html 我的服务器是500G.最重要的是/var分区一定要大(不论postfix邮件,还是LAMP的WEB 服务器等).最好是400G以上.具体的/boot 只要100M就足够了.下面是我的分区方案:硬盘500G 服务器分区的方案:分区类型 分区的实际大小/ 1G-2G (最少要150–250MB)/boot 32M-100M (启动分区,最多只要100M左

linux下bash特性和基础管理类命令(一)

bash 的基础特性 1.glob 通配字符         *多个任意字符         ?一个任意字符        []匹配指定范围内的任意单个字符       [^]匹配范围外的任意单个字符      [:lower:]小写字母   #中括号为字符集的一部分,使用时还要加中括号      [:upper:]大写字母     [:alnum:]所有字母     [:digit:]数字     [:space:]空白字符     [:punct:]标点符号 2.组合键     Ctrl+a

SQL2043N 与 linux的randomize_va_space特性

自从数据库服务器从redhat4.6升级到redhat5.5之后,在使用TSM备份的时候偶尔会出现SQL2043N 查看错误: [[email protected] ~]$ db2 ? SQL2043N SQL2043N Unable to start a child process or thread. Explanation: Unable to start up the child processes or threads required during the processing of

linux的shell特性三兼杂记

shell可以说是一个解释器,同时为代码运行提供了运行环境.并且提供了编程能力 shell没有自己的库,只是写命令调用操作内的小程序命令 bash特性中,bash不支持浮点数,把所有要存储的数据当做字符来进行. linux脚本其实就是一堆命令的堆砌,然后是用程序控制着脚本实施运行. 编程语言的基本结构: 数据,数据存储:变量,数组,等等 表达式:例如赋值语句 语句:顺序语句,选择语句等等 编程风格: 1.过程编程:以指令为中心,数据为指令服务 2.对象编程:以数据为中心,指令服务数据 编译型语言

Linux的systemd特性及gawk使用

1. 简述systemd的新特性及unit常见类型分析,能够实现编译安装的如nginx\apache实现通过systemd来管理 Systemd的新特性: 系统引导时实现服务并行启动: 按需激活进程: 系统状态快照: 基于依赖关系定义服务控制逻辑: 核心概念:unit unit由其相关配置文件进行标识.识别和配置:文件中主要包含了系统服务.监听的socket.保存的快照以及其它与init相关的信息: 这些配置文件主要保存在: /usr/lib/systemd/system /run/system

linux最新版本Centos7中应用程序的安装和RPM详解

一.linux应用程序基础 1.应用程序与系统命令的关系2.典型应用程序的目录结构3.常见的软件包封装类型4.RPM包管理工具5.一般命名格式6.查询rRPM软件包信息-q 查看已安装的软件包,-qa:查看所有已经安装的软件包-qi:某一个软件包详细信息,-ql 检查RPM软件包有哪些内容-qf:某一个RPM里的文件,-qc:查看RPM配置文件的位置-qd:RPM文档手册,-qp:查看未安装的软件包7.安装,升级,卸载RPM软件包-i:安装软件包仓库,-u:更新-f:查看某一文件目录属于RPM安

Linux系统基础特性

一.Linux操作系统说明 1.主流的Linux发行版 Linux发行版有数百种之多,常见的版本有以下几种: 1)RedHat:中国和美洲流行 Redhat 9.0个人版2003年停止更新àFedora Core:6个月更新一次 RedHat Enterprise Linux: RHEL --->社区版CentOS: Community ENTerprise OS 2)Slackware 版本 SuSE(商业.开源两种)--->欧洲流行 SLES:SuSE的商业版 OpenSuSE :SuSE

Linux的systemd特性及awk使用

1.简述systemd的新特性及unit常见类型分析,能够实现编译安装的如nginx\apache实现通过systemd来管理Systemd的新特性: 系统引导时实现服务并行启动: 按需激活进程: 系统状态快照: 基于依赖关系定义服务控制逻辑: 核心概念:unit unit由其相关的配置文件进行标识.识别和配置:文件中主要包含了系统服务.监听的socket.保存的快照以及其他与init相关的信息: /usr/lib/system /run/systemd/system /etc/systemd/

Linux之bash特性

什么是shell?字面意思:壳,充当人机交互的翻译官,当用户输入一条命令,shell就会解析.shell位于操作系统外层,负责与用户交互.shell类型:可以查看/etc/shells文件,bash shell作为系统的默认shell,比较流行,bash有众多特性.查看系统当前shell:#echo $SHELL 命令历史记录 命令/目录补全功能 命令行展开 命令的执行结果 快捷键 glob通配符 多命令执行 命令历史记录shell进程会将保存此前用户提交执行过的命令,保存在用户家目录:~/.b