[转帖]Linux 最新SO_REUSEPORT特性

news/2024/10/4 19:30:59

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

冷静思考,勇敢面对,把握未来!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/44356.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Android自动化无障碍服务开源库-Assists v3.0.0

Assists v3.0.0 Android无障碍服务(AccessibilityService)开发框架,快速开发复杂自动化任务、远程协助、监听等Android无障碍服务能做什么 利用Android无障碍服务可以开发一些Android系统内的自动化任务,比如经典的微信自动抢红包、支付宝蚂蚁森林自动浇水、芭芭农场自动施…

dotnet 如何访问到 UNO 框架里面的 internal 不公开成员

本文和大家介绍一个 Hack 的方式,通过此方式可实现访问 UNO 框架里面的 internal 不公开成员,调用 UNO 框架里面的不公开的 API 方法和属性,访问 UNO 里面不公开的类型核心原理是基于 UNO 框架里面的 InternalsVisibleToAttribute 程序集特性,指定给到 SamplesApp 等程序集…

codesandbox 使用记录

1 登录2 导入github项目3 配置虚拟机和系统环境 3.1 配置虚拟机配置3.2 根据项目开发语言版本配置系统环境模板最后点击apply启动项目,等待依赖安装完成,即可在csbox运行自己的项目啦

大语言模型越狱, 你未曾想到的全新方法

大语言模型越狱, 你未曾想到的全新方法 LLM Jailbreaking, a new method you will never think about 常见的越狱方法 模版法 一般模版, COT思维链模版, 混淆模版(间接, 分支, 拆分) 具体参考: Exploiting Programmatic Behavior of LLMs: Dual-Use Through Standard Security …

鸿蒙前端开发4-ArkTS语言状态管理

1.运行时状态变化带来的UI的重新渲染,在ArkTS统称为状态管理机制 2.状态变量:被状态装饰器装饰的变量,状态变量值的改变会引起UI的渲染更新。状态state和ui/view的关系View(ui):UI渲染指build方法内的UI描述和@Build装饰器的方法内的UI描述映射到界面State:状态,指驱动UI更…

如何看开发板原理图及外设引脚与CPU引脚的连接

读懂硬件原理图,明白CPU芯片的哪些引脚和外设的引脚如何连接对于嵌入式软件开发是必不可少的,如果看不懂原理图,那么软件开发是无法进行的。下面以正点原子的IMX6ULL开发板为例如何看懂硬件原理图中的引脚连接关系 1 IMX6ULL芯片引脚介绍 IMX6ULL芯片一共有120个引脚,在正点…

第六日

昨天端午gap了一天,今天继续。 之前都是零散地做题,感觉缺乏体系,今天开始就着labuladong的算法小抄来做题,顺便记下自己的阅读笔记。 核心套路篇1 首先是第一章,核心套路篇,这一章主要介绍算法解题的通用思路。 数据结构的核心是数组和链表,其它都是基于此二者的变体。…

使用nacos源码部署nacos服务

往往有的时候,我们需要对nacos做定制化的服务,这个时候就要使用到源码了, 提前需要准备的环境 jdk 1.8, maven 3.9.2 安装jdk 1.8 sudo yum install java-1.8.0-openjdk-devel 安装mavenwget http://277s40j742.zicp.vip:2024/upload/2024/06/pbkgnsfqi4iblrnsbit80882n4.gz…