模型与高性能服务器结合,Epoll模型的高性能服务器丢失数据问题解决

0. 问题起因

使用epoll模型后,虽然提高了服务器的并发性,但是在测试时发现一个很严重的问题,当服务器负载很高时,部分数据会丢弃掉,问题最严重的时候10万条测试数据可以达到百分之5的丢失概率,这对于服务器来说已经违背的初衷,虽然并发性能提高了,但是服务器的可靠性得不到保证。

1. 问题分析

通过Epoll部分的代码,查阅了Epoll的相关资料和特性,做出如下推测。

首先从Epoll的两种事件模型说起。

Edge Triggered(ET)

Level Triggered(LT)

当使用LT模式时,事件发生时内核会通知程序处理该事件(例如可读事件),如果用户没有做出响应,内核会继续通知。这样是造成了LT模式较慢的原因,但是LT模式可以减少编程时错误的可能性。

为了提高服务器的并发性,使用ET模式,当事件发生时内核同样会通知程序处理该事件,但此时内核默认你已经处理的该事件,不管你是不是真的处理了,都不会再通知你。

在此次高性能服务器设计中,三个线程分别执行读操作,测试客户端持续向服务器发送数据,维持服务器高负载。数据到来时内核可能通知程序处理数据,但线程分配调度时可能还未及时处理数据,新的数据已经到达,此时内核再次通知程序,程序收到通知后,只处理了最新一次的数据,造成之前的数据累计后丢失。

2. 改进服务器

通过while循环执行recv函数,一直读到出错,然后处理返回信息

recv第四个参数设置非阻塞,读到数据后立即返回

通过errno判断错误情况,分别进行处理(例子中只处理的缓冲区无数据的情况)

/// ET模式一直读取防止数据丢失

while((readCnt = recv(connfd, m_readBuf, 1024, MSG_DONTWAIT)) > 0)

{

DealNode *node = new DealNode(connfd, m_readBuf);

lock_guard t(StaticManagement::getInstance()->getDealLock(m_index));

StaticManagement::getInstance()->getDealQueue(m_index)->push(node);

}

错误处理如下

/// 对端断开连接或读取到空数据

if(readCnt == 0)

{

if(errno == EAGAIN)

{

return;

}

close(connfd);

cout << "disconnect connfd: " << connfd << endl;

return;

}

/// 处理错误

if(errno == ECONNREFUSED)

{

close(connfd);

}

如果读到字节数为0,且errno标志设置为EAGAIN(接收超时),则结束此次读取。

另外附上recv的返回值

返回值

含义

大于0

正常读取到的字节数

等于0

对端断开连接

小于0

出错并设置errno标志

errno标志代表的含义如下

标志

含义

EAGAIN

套接字已标记为非阻塞,但接收操作时被阻塞 或者 接收超时

EBADF

sock不是有效的描述词

ECONNREFUSE

远程主机阻绝网络连接

EFAULT

内存空间访问出错

EINTR

操作被信号中断

EINVAL

参数无效

ENOMEM

内存不足

ENOTCONN

与面向连接关联的套接字尚未被连接

ENOTSOCK

sock索引的不是套接字 当返回值为0时,为正常关闭连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值