使用webbench进行压力测试的时候,在baidu上一般来说正常。在自己的webserver上测试,有时会卡住而没有返回值。在修改为长连接的时候尤其会发生。
如下图
必须要ctrl+c终止。
github上也有这样的问题
长连接问题 · Issue #31 · linyacool/WebServer
找到可能被卡住的地方
测试时修改了部分源码进行打印,看每次1000个子进程中被阻塞了的进程数判断。
1:代码健壮性增加:从while (s = Socket(host, port) == -1){};
修改为
while (!timerexpired == 0 && (s = Socket(host, port)) == -1){};
避免反复创建socket一直失败陷入死循环而不理会时钟到期的回调函数早就把timerexpired
置为1,通知进程该结束了。
测试,没有明显好转。
2:修改socket函数,增加将创建的套接字使用fcntl设置fd为 O_NONBLOCK
非阻塞的代码。可以避免在read的时候阻塞,而服务器已经结束发送,从而无限等待下去。接着要对应修改前面的代码,当read返回-1,但是errno为EAGAIN的时候,不能计入fail,可能只是服务器响应慢了一点而以,设计为让他睡一下下,再继续。
if (i < 0 && errno == EAGAIN)
{
usleep(200);
continue;
}
测试,略有好转。
3:在测试时使用top查看,发现存在大量僵尸进程,卡住的时候僵尸进程还是很多。找到问题,有很多子进程是exit关闭的,父进程在fscanf,无法处理。导致clien—没有执行,无法跳出这个while循环。那么解决办法也就很明显了,给父进程也设置一个计时器与回调函数,到时间了就跳出这个循环,而不是使用while(1)死循环。
//测试时有时候webbench卡住,就应该是在这个while1循环里卡住的。
while (1)
{
/*
fscanf 函数原型为 int fscanf(FILE * stream, const char * format, [argument...]);
其功能为根据数据格式(format),从输入流(stream)中读入数据,
存储到argument中,遇到空格和换行时结束。
*/
pid = fscanf(f, "%d %d %d", &i, &j, &k);
if (pid < 2)
{
fprintf(stderr, "Some of our childrens died.\n");
break;
}
speed += i;
failed += j;
bytes += k;
/* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
if (--clients == 0)
break;
}
增加代码
static void father_alarm_handler(__attribute__((unused)) int signal)
{
time_up_hard = 1;
}
...
struct sigaction sa_father;
/* setup alarm signal handler */
sa_father.sa_handler = father_alarm_handler;
sa_father.sa_flags = 0;
if (sigaction(SIGALRM, &sa_father, NULL))
exit(3);
alarm(benchtime + 5); // after benchtime,then exit
...
while (!time_up_hard)
{
pid = fscanf(f, "%d %d %d", &i, &j, &k);
...
测试,可以按时跳出。显示有很多的failed。(自己的玩具webserver还是不够好)
以上的修改增加了webbench的健壮性。但是需要注意的是,即使在改装webbench前,使用baidu测试并不会出现问题,说明自己的webserver还是有待完善。