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

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

文章目录

  • C语言TCP服务器百万并发调参优化
    • 背景
    • 实验准备
    • 优化调参
      • 出现Connection refused错误
      • 客户端连接服务器时候产生Too many open files错误
      • 出现Cannot assign requested address错误
      • Connection timed out 错误
      • Cannot open /proc/meminfo:Too many open files in system file-max
      • 内存回收设置的调优
      • 杂项
      • 注意

背景

结合之前完成的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错误

TCP百万并发服务器优化调参_第1张图片

在这里插入图片描述

问题原因

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

TCP百万并发服务器优化调参_第2张图片

解决办法

修改open files数量,方法如下

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

TCP百万并发服务器优化调参_第3张图片

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

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

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

描述

在这里插入图片描述

原因和解决办法

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

出现Cannot assign requested address错误

描述

TCP百万并发服务器优化调参_第4张图片

原因分析

​ 首先,问题是不能分配客户端地址还是服务器地址?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;
    }

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

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

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

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

sudo vim /etc/sysctl.conf

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

TCP百万并发服务器优化调参_第5张图片

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

sudo sysctl -p

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

添加下面的语句

TCP百万并发服务器优化调参_第6张图片

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

sudo sysctl -p

内存回收设置的调优

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

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

    net.ipv4.tcp_mem = 252144 524288 786432
    

    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
    

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

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

    net.ipv4_tcp_rmem = 1024, 1024, 2048
    

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

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

杂项

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

注意

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

你可能感兴趣的:(C/C++,网络编程,服务器,tcp/ip,ubuntu)