标签:closed 公众号 enc bool 历史 create enter 指示 阻塞
本文始发于个人公众号:两猿社,原创不易,求个关注
使用Webbench对服务器进行压力测试,创建1000个客户端,并发访问服务器10s,正常情况下有接近8万个HTTP请求访问服务器。
结果显示仅有7个请求被成功处理,0个请求处理失败,服务器也没有返回错误。此时,从浏览器端访问服务器,发现该请求也不能被处理和响应,必须将服务器重启后,浏览器端才能访问正常。
通过查询服务器运行日志,对服务器接收HTTP请求连接,HTTP处理逻辑两部分进行排查。
日志中显示,7个请求报文为:GET / HTTP/1.0
的HTTP请求被正确处理和响应,排除HTTP处理逻辑错误。
因此,将重点放在接收HTTP请求连接部分。其中,服务器端接收HTTP请求的连接步骤为socket -> bind -> listen -> accept;客户端连接请求步骤为socket -> connect。
#include<sys/socket.h>
int listen(int sockfd, int backlog)
从上面的分析中可以看出,accept如果没有将队列中的连接取完,就绪队列中剩下的连接都得不到处理,也不能接收新请求,这个特性与压力测试的Bug十分类似。
//对文件描述符设置非阻塞
int setnonblocking(int fd){
int old_option=fcntl(fd,F_GETFL);
int new_option=old_option | O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
}
//将内核事件表注册读事件,ET模式,选择开启EPOLLONESHOT
void addfd(int epollfd,int fd,bool one_shot)
{
epoll_event event;
event.data.fd=fd;
event.events=EPOLLIN|EPOLLET|EPOLLRDHUP;
if(one_shot)
event.events|=EPOLLONESHOT;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblocking(fd);
}
//创建内核事件表
epoll_event events[MAX_EVENT_NUMBER];
int epollfd=epoll_create(5);
assert(epollfd!=-1);
//将listenfd设置为ET边缘触发
addfd(epollfd,listenfd,false);
int number=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
if(number<0&&errno!=EINTR)
{
printf("epoll failure\n");
break;
}
for(int i=0;i<number;i++)
{
int sockfd=events[i].data.fd;
//处理新到的客户连接
if(sockfd==listenfd)
{
struct sockaddr_in client_address;
socklen_t client_addrlength=sizeof(client_address);
//定位accept
//从listenfd中接收数据
int connfd=accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
if(connfd<0)
{
printf("errno is:%d\n",errno);
continue;
}
//TODO,逻辑处理
}
}
分析代码发现,web端和服务器端建立连接,采用epoll的边缘触发模式同时监听多个文件描述符。
从上面的定位分析,问题可能是错误使用epoll的ET模式。
尝试将listenfd设置为LT阻塞,或者ET非阻塞模式下while包裹accept对代码进行修改,这里以ET非阻塞为例。
for(int i=0;i<number;i++)
{
int sockfd=events[i].data.fd;
//处理新到的客户连接
if(sockfd==listenfd)
{
struct sockaddr_in client_address;
socklen_t client_addrlength=sizeof(client_address);
//从listenfd中接收数据
//这里的代码出现使用错误
while ((connfd = accept (listenfd, (struct sockaddr *) &remote, &addrlen)) > 0){
if(connfd<0)
{
printf("errno is:%d\n",errno);
continue;
}
//TODO,逻辑处理
}
}
}
将代码修改后,重新进行压力测试,问题得到解决,服务器成功完成75617个访问请求,且没有出现任何失败的情况。压测结果如下:
该Bug的出现,本质上对epoll的ET和LT模式实践编程较少,没有深刻理解和深入应用。
如果觉得有所收获,请顺手点个关注吧,你们的举手之劳对我来说很重要。
标签:closed 公众号 enc bool 历史 create enter 指示 阻塞
原文地址:https://www.cnblogs.com/qinguoyi/p/12355519.html