高并发服务器的限制有哪些,如何提高并发量

打印 上一主题 下一主题

主题 652|帖子 652|积分 1956

目录

前言
并发量限制因素 (五元组)
准备
测试并发量
思考局限
如何打破
超时连接问题何在
connection timeout的解决办法
实际开发中的处理方案
文章小结


前言

   本文纯粹就是小杰学习后端服务器开发的一个学习笔记系列.
小杰会尽量地将其梳理清楚, 大家一起学习,共同进步, 知识不分高低, 计算机的学习小杰认为也是一个    
量变   --->   质变    的过程
天道酬勤, 水滴石穿, 在不同的阶段就干好自己当前阶段力所能及之事,  至少是没有在寝室的床上瘫着消磨时光                                                 --------   愿大家都学有所成,所获
  并发量限制因素 (五元组)

五元组: (srcip, dstip, srcport, dstport, proto) 

  • 文件句柄, 文件描述符数量  open files
  • 系统内存限制 
  • 端口数量限制
  • 网络带宽的限制 (一般不做考虑)
  • 数据库的并发量限制
准备

先将 open files 修改到 100W的上限
查看单个进程可以打开的文件句柄的数目, open files的大小
命令:ulimit 
  1. ulimit -a 显示当前所有的资源限制
  2. ulimit -H 设置硬件资源限制
  3. ulimit -S 设置软件资源限制
  4. ulimit -n 设置进程最大打开文件描述符数
复制代码


我的已经被我自己修改为了100W的量级了 
修改方式:

  • 命令修改 : ulimit -n      缺陷:不是永久修改, 不涉及写磁盘, 重启shell之后修改消失
  • 修改配置文件  limits.conf 文件限制着用户可以使用的最大文件数,最大线程,最大内存等资源使用量。    vim /etc/security/limits.conf        涉及写磁盘, 每一次登录shell都会加载配置文件, 永久修改         配置文件记忆技巧, 资源使用限制涉及到系统安全, 故而在security中



测试并发量

首先针对之前写的reactor进行一个并发量的测试.
测试代码server代码在上一篇文章中:
epoll高度封装reactor,几乎所有可见服务器的底层框架_小杰312的博客-CSDN博客_小杰框架epoll高度封装reactor,几乎所有可见服务器的底层框架https://blog.csdn.net/weixin_53695360/article/details/123894158?spm=1001.2014.3001.5502
并发量:服务器可以承载的客户端的连接数量, 也就是可以维护的 sockfd的数量.
客户端并发量测试
初始版本测试结果如下:

思考局限

srcip 客户端的ip是固定的 srcport 客户端的可用端口数理论值是 65535
dstip +  dstport 固定, 服务器ip 跟 端口固定
 此时并发量可以达到2.8W, 然后报错不能分配地址了. 其实是客户端端口分配上限了.
如何打破

增加服务器端口数,  此时完全先从五元组确定唯一连接的方向切入,思考出可以增加服务端的监视窗口数量来提高并发量, 打破限制.       (多端口, 多窗口监视,有效提升客户接入量)


  • 将服务器的端口数开启到100个
  • 核心改变代码:
创建监视端口 init_listen_sock
  1. int init_sock(short port) {
  2.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  3.         if (sockfd == -1) {
  4.                 err_exit("socket");
  5.         }
  6.         sockaddr_in addr;
  7.         memset(&addr, 0, sizeof(addr));
  8.         addr.sin_port = htons(port);
  9.         addr.sin_family = AF_INET;
  10.         addr.sin_addr.s_addr = INADDR_ANY;
  11.         if (-1 == bind(sockfd, (SA*)&addr, sizeof(addr))) {
  12.                 err_exit("bind");
  13.         }
  14.         if (-1 == listen(sockfd, 5)) {
  15.                 err_exit("listen");
  16.         }
  17.         return sockfd;
  18. }
复制代码
  1.         //循环向eventloop中添加多监视窗口, 多port
  2.     int i = 0;
  3.         for (i = 0; i < LISTEN_PORT; ++i) {
  4.                 sockfd = init_sock(i + port);
  5.                 //将其加入到event_loop中
  6.                 struct epoll_event ev;
  7.                 ev.events = EPOLLIN;        //level 触发
  8.                 //注册监视事件
  9.                 struct sockitem* si = (struct sockitem*)malloc(sizeof(struct sockitem));
  10.                 si->sockfd = sockfd;
  11.                 si->callback = accept_cb;//设置事件处理器
  12.                 ev.data.ptr = si;
  13.                 epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, sockfd, &ev);
  14.         }
复制代码


  • 再测试
经过漫长等待之后它终于还是没有达到100W左右, 而是killed了, 因为内存限制被killed了

其实这个不是正常现象,我这里是因为内存限制而产生了killed, 实际上,内存限制没有打破的情况下也还是无法达到100W, 会出现    connection timeout 连接超时的错误 
超时连接问题何在

100 * 2.8W  服务端端口数(100端口) * 客户端端口数 (随机2.8W端口, 之前测试) 可以达到100W,所以接入量的限制不是五元组. 而是其他因素
针对connect的超时连接错误, 我们透过TCP三次握手去看, 问题在于服务端没有向客户端返回一个ACK, 导致了connect 超时.                                       --- server 的ACK为何没有到
此时的限制其实在于协议栈了. iptables, 一种过滤装置  防火墙

connection timeout的解决办法

修改  /etc/sysctl.conf 配置文件, 打破限制.
net.nf_conntrack_max 就是防火墙的限制
  1. fs.file-max = 1048576        
  2. net.nf_conntrack_max = 1048576
  3. net.ipv4.tcp_rmem =128 256 512
  4. net.ipv4.tcp_wmem =128 256 512
复制代码
至此其实可以完成百万接入量了, 只是我的服务器内存是在太小, 无法达到要求, 内存足够是可以跑到100W的.

实际开发中的处理方案

采用多进程的方式, 而不是多端口的方式.
测试代码:   MAX_PORT : 代表的是端口数, 与服务器端口数保持一致
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <sys/epoll.h>
  7. #include <errno.h>
  8. #include <netinet/tcp.h>
  9. #include <arpa/inet.h>
  10. #include <netdb.h>
  11. #include <fcntl.h>
  12. #define MAX_BUFFER                128
  13. #define MAX_EPOLLSIZE        (384*1024)
  14. #define MAX_PORT                1
  15. #define TIME_SUB_MS(tv1, tv2)  ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
  16. int isContinue = 0;
  17. static int ntySetNonblock(int fd) {
  18.         int flags;
  19.         flags = fcntl(fd, F_GETFL, 0);
  20.         if (flags < 0) return flags;
  21.         flags |= O_NONBLOCK;
  22.         if (fcntl(fd, F_SETFL, flags) < 0) return -1;
  23.         return 0;
  24. }
  25. // s设置好地址可复用
  26. static int ntySetReUseAddr(int fd) {
  27.         int reuse = 1;
  28.         return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
  29. }
  30. int main(int argc, char **argv) {
  31.         if (argc <= 2) {
  32.                 printf("Usage: %s ip port\n", argv[0]);
  33.                 exit(0);
  34.         }
  35.         const char *ip = argv[1];
  36.         int port = atoi(argv[2]);
  37.         int connections = 0;
  38.         char buffer[128] = {0};
  39.         int i = 0, index = 0;
  40.         struct epoll_event events[MAX_EPOLLSIZE];
  41.        
  42.         int epoll_fd = epoll_create(MAX_EPOLLSIZE);
  43.        
  44.         strcpy(buffer, " Data From MulClient\n");
  45.                
  46.         struct sockaddr_in addr;
  47.         memset(&addr, 0, sizeof(struct sockaddr_in));
  48.        
  49.         addr.sin_family = AF_INET;
  50.         addr.sin_addr.s_addr = inet_addr(ip);
  51.         struct timeval tv_begin;
  52.         gettimeofday(&tv_begin, NULL);
  53.         while (1) {
  54.                 if (++index >= MAX_PORT) index = 0;
  55.                
  56.                 struct epoll_event ev;
  57.                 int sockfd = 0;
  58.                 if (connections < 340000 && !isContinue) {
  59.                         sockfd = socket(AF_INET, SOCK_STREAM, 0);
  60.                         if (sockfd == -1) {
  61.                                 perror("socket");
  62.                                 goto err;
  63.                         }
  64.                         //ntySetReUseAddr(sockfd);
  65.                         addr.sin_port = htons(port+index);
  66.                         if (connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
  67.                                 perror("connect");
  68.                                 goto err;
  69.                         }
  70.                         ntySetNonblock(sockfd);
  71.                         ntySetReUseAddr(sockfd);
  72.                         sprintf(buffer, "Hello Server: client --> %d\n", connections);
  73.                         send(sockfd, buffer, strlen(buffer), 0);
  74.                         ev.data.fd = sockfd;
  75.                         ev.events = EPOLLIN | EPOLLOUT;
  76.                         epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev);
  77.                
  78.                         connections ++;
  79.                 }
  80.                 //connections ++;
  81.                 if (connections % 1000 == 999 || connections >= 340000) {
  82.                         struct timeval tv_cur;
  83.                         memcpy(&tv_cur, &tv_begin, sizeof(struct timeval));
  84.                        
  85.                         gettimeofday(&tv_begin, NULL);
  86.                         //计算出时间
  87.                         int time_used = TIME_SUB_MS(tv_begin, tv_cur);
  88.                         printf("connections: %d, sockfd:%d, time_used:%d\n", connections, sockfd, time_used);
  89.                         //每一次仅仅只是拿出来
  90.                         int nfds = epoll_wait(epoll_fd, events, connections, 100);
  91.                         for (i = 0;i < nfds;i ++) {
  92.                                 int clientfd = events[i].data.fd;
  93.                                 if (events[i].events & EPOLLOUT) {
  94.                                         sprintf(buffer, "data from %d\n", clientfd);
  95.                                         send(sockfd, buffer, strlen(buffer), 0);
  96.                                 } else if (events[i].events & EPOLLIN) {
  97.                                         char rBuffer[MAX_BUFFER] = {0};                               
  98.                                         ssize_t length = recv(sockfd, rBuffer, MAX_BUFFER, 0);
  99.                                         if (length > 0) {
  100.                                                 printf(" RecvBuffer:%s\n", rBuffer);
  101.                                                 if (!strcmp(rBuffer, "quit")) {
  102.                                                         isContinue = 0;
  103.                                                 }
  104.                                                
  105.                                         } else if (length == 0) {
  106.                                                 printf(" Disconnect clientfd:%d\n", clientfd);
  107.                                                 connections --;
  108.                                                 close(clientfd);
  109.                                         } else {
  110.                                                 if (errno == EINTR) continue;
  111.                                                 printf(" Error clientfd:%d, errno:%d\n", clientfd, errno);
  112.                                                 close(clientfd);
  113.                                         }
  114.                                 } else {
  115.                                         printf(" clientfd:%d, errno:%d\n", clientfd, errno);
  116.                                         close(clientfd);
  117.                                 }
  118.                         }
  119.                 }
  120.                 usleep(1 * 1000);
  121.         }
  122.         return 0;
  123. err:
  124.         printf("error : %s\n", strerror(errno));
  125.         return 0;
  126.        
  127. }
复制代码
文章小结


  • 做并发测试时候出现了问题,我们思考的方式是: 内存限制,  open files 文件句柄数限制,  五元组组合限制, 网络带宽, 数据库... 方向入手思考
  • 对于各种系统资源的限制, 可以通过修改配置文件的方式做出永久修改   /etc/security/limits.conf       +     /etc/sysctl.conf  
  • 五元组组合限制      防火墙限制
  • ulimit -a 查看所有的资源限制, free -h 查看内存限制, htop 动态观察CPU  + 内存占用情况, 便于分析异常

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

光之使者

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表