• TCP百万并发服务器优化调参


    C语言TCP服务器百万并发调参优化

    背景

    结合之前完成的C语言TCP服务端程序,其能够接受的最大并发数较低,故我们希望进行一些编码上或者是系统参数上的改变来实现提高TCP服务器的最大并发数

    注:这里说的最大并发数(或称最大并发量),**是指客户端的数量(**即通信socket的数量)

    而不是每秒请求的数量qps,注意区别!

    本实验最后的结果也并未达到百万并发,只接近80w,故本博客重点在于调优的参数和代码的修改有哪些。

    实验准备

    准备四台虚拟机 版本皆为 20.04

    服务器:4g内存,双核

    三台客户端:2g内存,单核

    若想要修改Ubuntu静态IP地址可参考:https://blog.csdn.net/baidu_39332177/article/details/123131601

    优化调参

    出现Connection refused错误

    描述

    当建立1024个socket的clientfd之后,别的客户端再想连接就出现Connection refused错误

    在这里插入图片描述

    在这里插入图片描述

    问题原因

    通过ulimit -a 查看服务器端默认的open files参数数量只有1024个

    在这里插入图片描述

    解决办法

    修改open files数量,方法如下

    • 方法一:临时修改,输入sudo ulimit -n 1048576,但是重启后又恢复原来设置值
    • 方法二:永久修改,输入 sudo vim /etc/security/limits.conf 然后在文件结尾位置添加相关设置

    在这里插入图片描述

    hard指警告的设定,可以超过这个值,但是超过后有警告

    soft指严格的设定,不允许超过这个值

    客户端连接服务器时候产生Too many open files错误

    描述

    在这里插入图片描述

    原因和解决办法

    ​ 由于我们只在服务器端修改了open files的数量,必须要在客户端也将open files设置为1048576

    出现Cannot assign requested address错误

    描述

    在这里插入图片描述

    原因分析

    ​ 首先,问题是不能分配客户端地址还是服务器地址?sockfd和网络地址之间有什么关系?

    ​ 根据socket可以找到一个五元组 socket–>(远程IP,远程端口,本机IP,本机端口,协议),socket和五元组是一对一关系,通过这个五元组可以通过recvsend函数来收发数据。

    所以该问题原因就是五元组的组合被耗尽,服务器的本机IP以及远程IP和远程端口号已经确定,所以唯一能够利用的就是增添本机端口数量来增加socket的数量。

    ​ 注意:本机端口数量最大只能有65535

    解决办法

    ​ 使用100个端口号与服务器地址和socket的绑定,得到100个监听的fd,并将其上epoll树,由epoll来监控事件到来,关键代码的修改如下:

    int epfd = epoll_create(1); // 1.创建一个监视事件的管理员fd
        int sockfds[MAX_PORT] = {0}; // listen fd集合
    
        int i = 0;
        //开MAX_PORT个端口进行监听
        for(i = 0;i < MAX_PORT; ++i)
        {
            //创建监听的文件描述符,相当于酒店门口迎宾的人员
            //为客户提供服务由其他服务员来做
            int sockfd = socket(AF_INET, SOCK_STREAM, 0);
            
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(struct sockaddr_in));//清空结构体  
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port + i); //主机字节序转换为网络字节序 8888, 8889....8987
            addr.sin_addr.s_addr = INADDR_ANY;
            
    
            if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
            {
                perror("bind");
                return -2;
            }
            //利用sockfd进行监听
            //第二个参数指定能处理的最大连接请求
            if(listen(sockfd, 5) < 0)
            {
                perror("listen");
                return -3;
            }
            
            printf("tcp server listen on port: %d\n", port + i);
    
            struct epoll_event ev;
    
            //有数据到来socketfd,表示可读了,则会触发EPOLLIN
            //对端(客户端)读取了一些数据走,则表示可以往这个socketfd写了,则会触发EPOLLOUT
            ev.events = EPOLLIN;
            ev.data.fd = sockfd;
    
            //EPOLL_CTL_ADD:在epoll的监视列表中添加一个文件描述符(即参数fd),指定监视的事件类型(参数event)
            epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);                           
    
            sockfds[i] = sockfd;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    Connection timed out 错误

    描述

    这个问题在ubuntu20.04上不存在,但还是记录一下。

    在这里插入图片描述

    原因与解决方案

    首先查看file-max参数值是否不足100w

    file-max : sets the maximum number of file-handles that the Linux kernel will allocate.

    jyhlinux@ubuntu:~$ cat /proc/sys/fs/file-max
    9223372036854775807
    
    • 1
    • 2

    已经大于100w,故接着检查nf_conntrack_max(设置连接跟踪的最大值)

    jyhlinux@ubuntu:~$ sudo modprobe ip_conntrack  #这句用来启动ip_conntrack,不加则下面语句失败
    jyhlinux@ubuntu:~$ cat /proc/sys/net/netfilter/nf_conntrack_max 
    262144
    
    
    • 1
    • 2
    • 3
    • 4

    这里我们可以看出连接跟踪最大值只有262144,没有到100w,所以解决方法就是将该参数设置为100w之上

    sudo vim /etc/sysctl.conf
    
    • 1

    net.nf_conntrack_max = 1048576添加进配置文件

    在这里插入图片描述

    然后输入下方语句生效配置

    sudo sysctl -p
    
    • 1

    Cannot open /proc/meminfo:Too many open files in system file-max

    遇到该问题说明file-max不够大,在etc/sysctl.conf修改参数即可

    jyhlinux@ubuntu:~$ sudo modprobe ip_conntrack  #这句用来启动ip_conntrack,不加则下面语句失败
    jyhlinux@ubuntu:~$ sudo vim /etc/sysctl.conf
    
    • 1
    • 2

    添加下面的语句

    在这里插入图片描述

    然后输入下面的语句生效配置

    sudo sysctl -p
    
    • 1

    内存回收设置的调优

    以下配置代码都是添加到etc/sysctl.conf文件中,注意开始要加上sudo modprobe ip_conntrack

    1. 第一个参数:tcp协议栈内存

      net.ipv4.tcp_mem = 252144 524288 786432
      
      • 1

      tcp_mem(3个INTEGER变量):low, pressure, high

      low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
      pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
      high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”

    2. 第二个参数:tcp发送缓冲区

      net.ipv4.tcp_wmem = 1024 1024 2048
      
      • 1

      表示tcp连接(socket)的发送(write)缓存区的最小、默认、最大值,分别为1KB ,1KB,2KB

    3. 第三个参数:tcp接收缓冲区

      net.ipv4_tcp_rmem = 1024, 1024, 2048
      
      • 1

      表示tcp连接(socket)的接受(recive)缓存区的最小、默认、最大值,分别为1KB ,1KB,2KB

    这样设置之后,默认的1个socket的file descriptor大小为2KB(rmem + wmem),百万个fd就大概是2g内存。

    杂项

    • 最后测试的时候,我这个配置也只能最多到82万左右,不知道什么原因,也加大了客户端和服务端的内存
    • 大量客户机断开连接宕机时候,处理器使用率会飙升
    • 内存和CPU使用率最好维持在80%一下

    注意

    本实验主要目的是了解百万并发所需要的优化,故代码就不详加给出了。如果面试时候,如果别人问起这个服务器调优,就和面试官说清楚解决了哪几个问题,不要没有达到100w就唯唯诺诺,不敢回答。

  • 相关阅读:
    miniprogram-ci 使用说明
    【目标跟踪-卡尔曼滤波】基于分布式Kalman滤波跟踪运动目标附Matlab代码
    【操作系统】聊聊Linux软中断
    react native 0.70版本使用ant-design-mobile-rn及icons字体图标库
    python中yield浅析
    IOS企业IPA软件证书 苹果签名证书 有效期到2026年
    labml-nn:带注释的 pyTorch 论文实现
    【小尘送书-第五期】《巧用ChatGPT快速提高职场晋升力》用ChatGPT快速提升职场能力,全面促进自身职业发展
    看表情包学Linux:基本指令介绍
    网课答案查询单页源码+免费题库api接口
  • 原文地址:https://blog.csdn.net/qq_42120843/article/details/126086526