当前位置:   article > 正文

epoll服务器百万并发测试

epoll服务器百万并发测试

 百万并发

服务器百万并发是指能够承载的客户端的数量,也就是说可以承接100w个客户端的连接。通常来说服务器的并发量还与业务、与后台数据库的承载量有关,本文中只考虑做到百万连接建立,不做业务处理.。

测试准备

相应的服务器请参考前文:《用反应器模式和epoll构建百万并发服务器》。

所需的客户端请参考文章末尾。

需要的环境,1台8G8核的ubuntu(配置服务器),3台4G4核的ubuntu(配置客户端,每个客户端负责的连接数尽量平均,也就是33.33w)。如果电脑资源足够,性能够好,建议把配置做大些,问题会少不少。

解决连接上限问题

先用一台客户端尝试向服务器发起大量连接,发现报错。

再看看服务器的情况,同样是报错。

我们用ulimit -a查看服务器情况发现是一个进程最多能打开1024个文件,也就是建立1024个文件描述符。于是用ulimit -n解决,客户端和服务器都需要解决这些问题。

解决五元不组问题

服务器每建立一个连接,都会在内核中建立一个tcp控制块,简称tcb。tcb包含了以下元素:源ip,目的ip,源port,目的port,协议类型(tcp,udp等)。

通常协议类型和目的ip(服务器ip)是固定下来的。百万并发确保每个连接都对应剩下3个元素的不同的排列组合,若不能保证,产生的问题叫“五元不组”。

源port一般不被客户端指定,一台机子可用的port大概是(65535-1024)个,1024是因为部分非用户主动空控制的程序也在使用端口,在高并发下情况下1024-65535可能会无法用尽。

因此解决“五元不组”通常由以下几种解法:

  1. 源ip多样化:多准备几个不同ip的客户端。

  2. 目的port多样化:让服务器多开几个listen port并且负载均衡。

  3. 源port多样化,修改/etc/sysctl.conf文件,明确指定客户端在本地的端口的可用范围。

本文中两种方法都用,基于解法1配置了3台不同的ubuntu,每台上都有1个客户端。基于解法2服务器开了20个监听端口,并且客户端循环地进行connect。

基于解法3,则需要修改客户端所在的ubuntu的配置文件。

如图,在相关配置文件的末尾添加一条语句:

解决内存泄漏的问题

服务器每建立一个clientfd,都需要相应的数据结构储存clinetfd。普通的数组往往容易发生越栈从而发生段错误,不了解的人往往会以为这是因为代码的逻辑错误。我们在服务器中建立了百万长度的数组进行存储,不够优雅但是有效。

解决效率受限的问题

在并发连接的时候,经常连着连着就发现,每一千个连接的耗时突然暴增,这是受到了系统级别的文件描述符数量的限制。也需要调整。往客户端的配置文件里添加这句话。

该命令与ulimit -n的差别在于作用的级别不同,前者作用于系统层面,需要root权限,后者作用于当前用户的会话级别。

解决进程制终止问题

进程被强制终止可能发生在客户端,也有可能发生在服务器。

有些机子可能会发生服务器进程CPU和内存消耗过大,被强制kill掉的情况。这是由两种解法:

  1. 把配置做高,内存更大。
  2. 修改内核tcp协议栈内存、发送缓冲区、接收缓冲区的大小。

CPU和内核消耗的监控命令:htop

在conf文件中可以继续添加以下语句,分别用于修改内核tcp协议栈内存、单个连接发送缓冲区、单个连接接收缓冲区的最小值、默认值、最大值,可以逐步逐步地进行测试什么数据合适。

注意,也有可能是客户端配置不够高,发生了崩溃,导致服务器大量断连从而也崩溃。也有可能是因为进行被强制终止,内存被强制回收导致的。重点是用htop观察谁的内存爆了。

成功完成百万并发测试

测试用客户端代码

  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. #include <sys/time.h>
  13. #include <unistd.h>
  14. #define MAX_BUFFER 128
  15. #define MAX_EPOLLSIZE (384*1024)
  16. #define MAX_PORT 20
  17. #define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
  18. int isContinue = 0;
  19. static int ntySetNonblock(int fd) {
  20. int flags;
  21. flags = fcntl(fd, F_GETFL, 0);
  22. if (flags < 0) return flags;
  23. flags |= O_NONBLOCK;
  24. if (fcntl(fd, F_SETFL, flags) < 0) return -1;
  25. return 0;
  26. }
  27. static int ntySetReUseAddr(int fd) {
  28. int reuse = 1;
  29. return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
  30. }
  31. int main(int argc, char **argv) {
  32. if (argc <= 2) {
  33. printf("Usage: %s ip port\n", argv[0]);
  34. exit(0);
  35. }
  36. const char *ip = argv[1];
  37. int port = atoi(argv[2]);
  38. int connections = 0;
  39. char buffer[128] = {0};
  40. int i = 0, index = 0;
  41. struct epoll_event events[MAX_EPOLLSIZE];
  42. int epoll_fd = epoll_create(MAX_EPOLLSIZE);
  43. strcpy(buffer, " Data From MulClient\n");
  44. struct sockaddr_in addr;
  45. memset(&addr, 0, sizeof(struct sockaddr_in));
  46. addr.sin_family = AF_INET;
  47. addr.sin_addr.s_addr = inet_addr(ip);
  48. struct timeval tv_begin;
  49. gettimeofday(&tv_begin, NULL);
  50. while (1) {
  51. if (++index >= MAX_PORT) index = 0;
  52. struct epoll_event ev;
  53. int sockfd = 0;
  54. if (!isContinue) {
  55. sockfd = socket(AF_INET, SOCK_STREAM, 0);
  56. if (sockfd == -1) {
  57. perror("socket");
  58. goto err;
  59. }
  60. //ntySetReUseAddr(sockfd);
  61. addr.sin_port = htons(port+index);
  62. if (connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
  63. perror("connect");
  64. goto err;
  65. }
  66. //ntySetNonblock(sockfd);
  67. ntySetReUseAddr(sockfd);
  68. //sprintf(buffer, "Hello Server: client --> %d\n", connections);
  69. //send(sockfd, buffer, strlen(buffer), 0);
  70. ev.data.fd = sockfd;
  71. ev.events = EPOLLIN;
  72. epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev);
  73. connections ++;
  74. }
  75. //connections ++;
  76. if (connections % 1000 == 999) {
  77. struct timeval tv_cur;
  78. memcpy(&tv_cur, &tv_begin, sizeof(struct timeval));
  79. gettimeofday(&tv_begin, NULL);
  80. int time_used = TIME_SUB_MS(tv_begin, tv_cur);
  81. printf("connections: %d, sockfd:%d, time_used:%d\n", connections, sockfd, time_used);
  82. int nfds = epoll_wait(epoll_fd, events, connections, 100);
  83. for (i = 0;i < nfds;i ++) {
  84. int clientfd = events[i].data.fd;
  85. if (events[i].events & EPOLLOUT) {
  86. sprintf(buffer, "data from %d\n", clientfd);
  87. send(sockfd, buffer, strlen(buffer), 0);
  88. } else if (events[i].events & EPOLLIN) {
  89. char rBuffer[MAX_BUFFER] = {0};
  90. ssize_t length = recv(sockfd, rBuffer, MAX_BUFFER, 0);
  91. if (length > 0) {
  92. printf(" RecvBuffer:%s\n", rBuffer);
  93. if (!strcmp(rBuffer, "quit")) {
  94. isContinue = 0;
  95. }
  96. } else if (length == 0) {
  97. printf(" Disconnect clientfd:%d\n", clientfd);
  98. connections --;
  99. close(clientfd);
  100. } else {
  101. if (errno == EINTR) continue;
  102. printf(" Error clientfd:%d, errno:%d\n", clientfd, errno);
  103. close(clientfd);
  104. }
  105. } else {
  106. printf(" clientfd:%d, errno:%d\n", clientfd, errno);
  107. close(clientfd);
  108. }
  109. }
  110. }
  111. usleep(100);
  112. }
  113. return 0;
  114. err:
  115. printf("error : %s\n", strerror(errno));
  116. return 0;
  117. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/91938
推荐阅读
相关标签
  

闽ICP备14008679号