ab压测之apr_poll: The timeout specified has expired (70007)

本文介绍如何使用ab命令进行并发测试时遇到的超时问题及其解决方案。通过增加连接保持活动选项(-k),成功解决了并发测试中的超时错误,有效提升了测试效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用工具:

测试公司接口
  ab命令做25600个用户并发发送请求,测试吞吐率、用户等待时间、请求处理时间、服务器的负载,命令如下:
  ab -c 100 -n 25600 http://storm.cfpu.com/saas-web-test/statistics/helloworld3.do?aa=1111

输入命名后,提示apr_poll: The timeout specified has expired (70007)

解决方案:在命令行中加-k 使得connection keep alive
 
ab -c 100 -n 12800 -k http://storm.cfpu.com/saas-web-test/statistics/helloworld3.do?aa=111

问题解决


代码高并发卡死原因分析根本原因:信号处理与计数同步问题信号丢失风险SIGCHLD信号是非排队的,当多个子进程同时退出时,可能只触发一次信号处理高并发场景下(5000并发),大量子进程同时退出,信号处理函数无法及时处理所有僵尸进程计数更新延迟 Check and Limit Children 检查并限制子进程 当num_children=500时进入等待在sleep期间,即使有子进程退出,主进程仍在sleep状态无法立即accept新连接信号处理函数更新num_children,但主进程不会重新检查计数(直接continue)竞争条件 High Concurrency Timing Issue 高并发时序问题 具体问题表现连接队列溢出默认监听队列长度(SOMAXCONN)通常为1285000并发远超队列容量,新连接被丢弃输出中反复出现Reached max children表明系统持续过载进程创建瓶颈Windows下fork()模拟效率较低(通过WSL/Cygwin)500进程上限 + 高频创建/销毁导致调度开销剧增输出显示的证据 log Child 83948 exited with status 0 # 子进程退出 New child process 83949 (total: 500) # 立即创建新进程 Reached max children (500), waiting… # 立刻再次达到上限 表明计数更新和连接接受存在时间差 .\ab.exe -n 50000 -c 5000 http://192.168.64.10/ This is ApacheBench, Version 2.3 <$Revision: 1923142 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 192.168.64.10 (be patient) Completed 5000 requests Completed 10000 requests Completed 15000 requests Completed 20000 requests Completed 25000 requests Completed 30000 requests Completed 35000 requests Completed 40000 requests Completed 45000 requests apr_pollset_poll: The timeout specified has expired (70007) Total of 49266 requests completed 请基于以上问题,考虑到我的代码运行在Linux虚拟机中,对我的代码进一步优化,要求不改变多进程实现的根本,尽可能小的修改,给出详细的修改逻辑和解释 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/wait.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <netinet/in.h> #include <signal.h> #include “http.h” #include “multiProcess.h” #define MAX_CHILDREN 500 // 最大并发子进程数 #define MAX_WAIT_TIME 5 // 等待子进程退出的最长时间() static volatile sig_atomic_t num_children = 0; // 信号处理函数:回收僵尸进程 void sigchld_handler(int sig) { int saved_errno = errno; pid_t pid; int status; // 非阻塞方式回收所有已退出的子进程 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Child %d exited with status %d\n", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Child %d killed by signal %d\n", pid, WTERMSIG(status)); } num_children--; } errno = saved_errno; } // 信号处理函数:处理SIGTERM/SIGINT void sigterm_handler(int sig) { printf(“Received signal %d, shutting down…\n”, sig); // 这里可以添加清理代码 exit(EXIT_SUCCESS); } int multiProcessEntry() { int server_sock = -1; int port = 80; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); // 设置信号处理 struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(EXIT_FAILURE); } // 设置终止信号处理 signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE server_sock = startup(&port); printf("HTTP server running on port %d\n", port); // 设置监听套接字为非阻塞 int flags = fcntl(server_sock, F_GETFL, 0); fcntl(server_sock, F_SETFL, flags | O_NONBLOCK); while (1) { // 检查进程数限制 if (num_children >= MAX_CHILDREN) { // 等待部分子进程退出 printf("Reached max children (%d), waiting...\n", MAX_CHILDREN); sleep(1); continue; } int client_sock = accept(server_sock, (struct sockaddr *)&client_name, &client_name_len); if (client_sock == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 没有新连接,短暂休眠后重试 usleep(10000); // 10ms continue; } perror("accept failed"); continue; } pid_t pid = fork(); if (pid < 0) { perror("fork failed"); close(client_sock); } else if (0 == pid) { // 子进程 // 关闭不需要的文件描述符 close(server_sock); // 处理请求 accept_request(client_sock); // 关闭客户端socket close(client_sock); // 子进程退出 exit(EXIT_SUCCESS); } else { // 父进程 // 关闭客户端socket(父进程不需要) close(client_sock); // 增加子进程计数 num_children++; printf("New child process %d (total: %d)\n", pid, num_children); } } // 等待所有子进程退出 while (num_children > 0) { printf("Waiting for %d children to exit...\n", num_children); sleep(1); } close(server_sock); return EXIT_SUCCESS; }
08-16
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值