webserver使用webbench工具连续压测失败

2024-08-27 21:52

本文主要是介绍webserver使用webbench工具连续压测失败,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考资料:一次压力测试Bug排查-epoll使用避坑指南 - 两猿社 - 博客园 (cnblogs.com)

Bug复现

使用Webbench对服务器进行压力测试,创建1000个客户端,并发访问服务器10s。

结果显示仅有少数几个请求被成功处理,0个请求处理失败,性能很差,服务器也没有返回错误。此时,从浏览器端访问服务器,发现该请求也不能被处理和响应,必须将服务器重启后,浏览器端才能访问正常。

原因

当代码执行完listen()函数后,但未执行accept()函数前,Linux内核会为任何一个监听套接口维护两个队列,每个队列都是存储文件描述符fd:

  • 未完成连接的队列:该队列的文件描述符,正在等待TCP三次握手完成,还处在 SYN_RCVD 状态
  • 已完成连接的队列:该队列的文件描述符,已经完成TCP三次握手,处在 ESTABLISHED 状态

当代码执行accept()函数时,正常情况下,内核从处于 ESTABLISHED 状态的已完成连接队列的头部,取出一个文件描述符进行处理。

但是也会出现异常情况,比如:

  • 如果这个已完成连接的队列为空,accept函数就会阻塞,直到队列中有新的文件描述符加入。
  • 如果,服务器不能及时调用 accept取走队列中已完成的连接,当已完成连接队列满掉后,未完成连接的队列中剩下的连接都得不到处理,同时会禁止新的连接。

从上面的分析中可以看出,accept如果没有将已完成连接队列中的连接取完,未完成连接队列中剩下的连接都得不到处理,也不能接收新请求,这个特性与压力测试的Bug十分类似

由于我的代码中使用的是epoll+ET模式,当epoll_wait检测到epoll文件描述符有事件发生,只会通知一次,随后内核就不会发送通知了,如果一直不对这个fd操作,内核就不会发送更多的通知

因此应用程序必须立即处理该事件,而且必须要一次性将数据读取完

而在错误的代码中,我使用的是

    if(sockfd == listenfd){struct sockaddr_in client_address;socklen_t client_addresslen = sizeof(client_address);int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addresslen);......}

如果我们使用压测工具测试时,会建立多个客户端连接,epoll_wait()函数会返回 1,表示有一个fd需要处理(实际上就是listenfd)

同时,已完成队列会在这一瞬间爆满,当执行int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addresslen),只会取出一个已完成的连接(剩下还有很多个没取),但内核却不会发送通知处理 listenfd(因为内核认为你已经处理完了listenfd),到下一次的epoll_wait()时,已完成队列爆满,不再接收新的连接,listenfd没有新的通知消息,状态不变,会卡在epoll_wait()

因此,在此之后,由于已完成连接队列爆满而得不到处理,再从浏览器端访问服务器,会发现无法连接


综上所述,我们需要一次性读完已完成连接队列中的连接

解决方法

修改前

for(int i = 0; i < number; i++)
{int sockfd = events[i].data.fd;// 有新的客户端连接if(sockfd == listenfd){struct sockaddr_in client_address;socklen_t client_addresslen = sizeof(client_address);int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addresslen);if(connfd < 0){// printf("errno is %d\n", errno);LOG_ERROR("%s:errno is:%d", "accept error", errno);continue;}......}......
}

修改后

for(int i = 0; i < number; i++)
{int sockfd = events[i].data.fd;struct sockaddr_in client_address;socklen_t client_addresslen = sizeof(client_address);int connfd;// 有新的客户端连接if(sockfd == listenfd){while ((connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addresslen)) > 0){if(connfd < 0){// printf("errno is %d\n", errno);continue;}......}......}......
}

这篇关于webserver使用webbench工具连续压测失败的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1112880

相关文章

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚

使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)

《使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)》PPT是一种高效的信息展示工具,广泛应用于教育、商务和设计等多个领域,PPT文档中常常包含丰富的图片内容,这些图片不仅提升了... 目录一、引言二、环境与工具三、python 提取PPT背景图片3.1 提取幻灯片背景图片3.2 提取

使用Python实现图像LBP特征提取的操作方法

《使用Python实现图像LBP特征提取的操作方法》LBP特征叫做局部二值模式,常用于纹理特征提取,并在纹理分类中具有较强的区分能力,本文给大家介绍了如何使用Python实现图像LBP特征提取的操作方... 目录一、LBP特征介绍二、LBP特征描述三、一些改进版本的LBP1.圆形LBP算子2.旋转不变的LB

Maven的使用和配置国内源的保姆级教程

《Maven的使用和配置国内源的保姆级教程》Maven是⼀个项目管理工具,基于POM(ProjectObjectModel,项目对象模型)的概念,Maven可以通过一小段描述信息来管理项目的构建,报告... 目录1. 什么是Maven?2.创建⼀个Maven项目3.Maven 核心功能4.使用Maven H

Python中__init__方法使用的深度解析

《Python中__init__方法使用的深度解析》在Python的面向对象编程(OOP)体系中,__init__方法如同建造房屋时的奠基仪式——它定义了对象诞生时的初始状态,下面我们就来深入了解下_... 目录一、__init__的基因图谱二、初始化过程的魔法时刻继承链中的初始化顺序self参数的奥秘默认

SpringBoot使用GZIP压缩反回数据问题

《SpringBoot使用GZIP压缩反回数据问题》:本文主要介绍SpringBoot使用GZIP压缩反回数据问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot使用GZIP压缩反回数据1、初识gzip2、gzip是什么,可以干什么?3、Spr

Spring Boot 集成 Quartz并使用Cron 表达式实现定时任务

《SpringBoot集成Quartz并使用Cron表达式实现定时任务》本篇文章介绍了如何在SpringBoot中集成Quartz进行定时任务调度,并通过Cron表达式控制任务... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启动 Sprin

MySql match against工具详细用法

《MySqlmatchagainst工具详细用法》在MySQL中,MATCH……AGAINST是全文索引(Full-Textindex)的查询语法,它允许你对文本进行高效的全文搜素,支持自然语言搜... 目录一、全文索引的基本概念二、创建全文索引三、自然语言搜索四、布尔搜索五、相关性排序六、全文索引的限制七

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文