免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 12449 | 回复: 14
打印 上一主题 下一主题

linux-epoll处理大流量数据问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-10-28 23:14 |只看该作者 |倒序浏览
我参考lighttpd写了一个基于epoll的多进程服务器,epoll采用ET非阻塞工作方式,epoll_wait和监听连接的代码都是在main线程中
(使用epoll的方式基本同lighttpd,看过lighttpd源码的应该清楚以上工作方式,不同的是自己加了多线程来处理读取数据后的业务逻辑)
当客户端发送的小流量数据时,epoll能正常工作
但当客户端发送大的流量数据时,比如连续发送大于30000个字节的数据,epoll读取数据就有问题,
是不是因为读取数据来不及导致epoll接受缓冲区不够用了?
我的read函数是这样的:
int FDRecv(FDConnection *con, char *buf, int bufsize)
{
        int len = 0;
        int toread;
        int s = 0;
        int fd = con->fd;
       
        if (ioctl(fd, FIONREAD, &toread))
        {
                return -1;
        }

        len = read(fd, buf, bufsize);
        if ( len < 0)
        {
                printf("read socket %d error: %s\n", fd, strerror(errno));
                con->is_readable = 0;
                // 由于是非阻塞模式,当errno为EAGAIN时,表示当前缓冲区无数据可读了
                if (EAGAIN == errno)
                {
                        return 0;
                }
                if (EINTR == errno)
                {
                        con->is_readable = 1;
                        return 0;
                }
                con->FDConnectionSetReadState(READ_DRIVE_FD_ERROR);
                return -1; // -1出错
        }
        else if (0 == len) // 客户端关闭了连接
        {
                con->is_readable = 0;
                con->FDConnectionSetState(CON_STATE_READ);
                con->FDConnectionSetReadState(READ_DRIVE_FD_CLOSE);
                return -2; // -2关闭连接
        }
        else if (len < toread) // 读取的比预期的要小,则需要等待下一次读事件
        {
                con->is_readable = 0;
        }
               
        //printf("toread = %d, len = %d\n", bufsize, len);
        return 0;
}
每当epoll_wait一个读事件,则调用该函数。以上读取代码段有问题吗??
ps:当采用gdb单步调试时,epoll工作又没问题,郁闷。。

[ 本帖最后由 butoo 于 2009-10-29 20:41 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-10-29 14:23 |只看该作者

回复 #1 butoo 的帖子

你这个代码好像不太好。
你的循环读取数据的代码在哪里?如果一次read读不出所有的数据(比如你说的30000), 你需要等到线程的下一次调度吗?

论坛徽章:
0
3 [报告]
发表于 2009-10-29 20:38 |只看该作者

回复 #2 youshuang 的帖子

这个是在LT模式下读取数据的,没有用到循环读。随后我让epoll工作在ET模式下,更改了循环读,结果还是一样:
通过ioctl(con->fd, FIONREAD, &bufsize))获得要读取的bufsize,

int FDRecv(FDConnection *con, char *buf, int bufsize)
{
        int len = 0;
        int s = 0;
        int fd = con->fd;
       
        while (len < bufsize)
        {
                s = read(fd, &buf[len], bufsize - len);
                if(s < 0)//出错
                {
                        con->is_readable = 0;
                        if (errno == EAGAIN)
                        {
                                return 0;
                        }
                        //被中断执行
                        if (errno == EINTR)
                        {
                                con->is_readable = 1;
                                return 0;
                        }
                        con->FDConnectionSetReadState(READ_DRIVE_FD_ERROR);
                        return -1; //-1出错
                       
                }
                else if (0 == s) //客户端关闭了连接
                {
                        con->is_readable = 0;
                        con->FDConnectionSetState(CON_STATE_READ);
                        con->FDConnectionSetReadState(READ_DRIVE_FD_CLOSE);
                        return -2;
                }
                else
                {
                        len += s;
                }
        }
}
采用以上循环读,当epoll在ET模式下,当有IN事件发生,缓冲区数据可以读出来(前提是客户端连续不间断发给服务器的数据缓冲区不大于8K),当流量很大时(例如连续不间断发10k)就出错了。
可以肯定一点是因为流量太大引起问题的。但我还没找出问题在哪里。期待高手解决!

论坛徽章:
0
4 [报告]
发表于 2009-10-30 09:51 |只看该作者
有问题,while (len < bufsize) , 当len == bufsize后你返回啥了, 再说你这个函数根本没有返回接收了多少个字节的数据,调用者可以分析那个缓冲区中的数据来确定buf里面到底有多少数据?

论坛徽章:
0
5 [报告]
发表于 2009-10-30 10:45 |只看该作者

回复 #4 学与思 的帖子

在流量不大的时候,epoll是正确的,读取缓冲区的函数可以正常工作,当流量大时,就不正确了。
我怀疑,是不是当流量大了后,在两次IN事件产生的间隔事件内,还没来得及从缓冲区中读出上一次IN事件时缓冲区内的数据。所以当下一次IN事件来后,根据ioctl来确定要读取多少数据可能不正确了,因为此时缓冲区的数据还包括上一次IN事件没来得及读完的数据??

再次说明,流量不大时读取函数是正确的,流量大时才会出错,希望大家找到症结所在。

论坛徽章:
0
6 [报告]
发表于 2009-10-30 11:06 |只看该作者
自己再顶。
当单步调试的时候,不管流量有多大都是对的
可能在单步模式下,在两次IN事件时间间隔内有足够的时间可以将缓冲区数据读完。
有人肯定也遇到过这种情况,希望得到帮助!

论坛徽章:
0
7 [报告]
发表于 2009-10-30 11:35 |只看该作者
很简单,你的程序流程或则逻辑没有对,把你epoll那段代码贴出来看

论坛徽章:
0
8 [报告]
发表于 2009-10-31 18:00 |只看该作者

回复 #7 学与思 的帖子

我的服务逻辑是这样的:
首先创建监听fd,设置为非阻塞,epoll工作在ET模式,然后启用一个线程执行监听和事件轮询
有事件产生则通过产生事件的fd(可能为监听fd,可能为连接fd)获取回调函数,执行创建新连接或者处理读数据,
以上方式基本同lighttpd的模式。
当数据读出后,则将数据打包成一个job,然后将job缓存到hash表中,并不急于处理。
当有job缓存到hash表,则会唤醒线程池中的工作线程,找到待执行的job,然后解析job中的命令和数据(主要是操作数据库,比如大流量时就是执行往数据库插入blob的操作),最后执行这些命令。
逻辑基本这样,有什么问题吗?
难道需要另外用线程进行数据读,这样才能来得及读出数据??

如果是流量大引起来的问题,但我的程序一直在尽力从缓冲区中读出数据并打包成一个job,然后将job缓冲到一个hash表中,最后采用3个工作线程从hash表中取出job执行。按理说如果一直有数据到达,程序会很快读出缓冲区的数据。
咋办呢~???????????

论坛徽章:
0
9 [报告]
发表于 2009-11-09 21:03 |只看该作者
楼主问题没描述清楚。什么是“读取数据有问题”?
是没读到想要的数目,还是系统出错了,errno是多少,这些关键的信息。。。

怀疑发送太快处理不过来,可以看看发送方出问题了么(堵了),是不是退出了等等。不一定是epoll的问题。Tcp有流量控制,这边buffer满了那边就发不过来。
关键是你这边的errno

论坛徽章:
0
10 [报告]
发表于 2009-11-10 11:26 |只看该作者
我觉得是你的Read有问题,改成这样试试。
int FDRecv(FDConnection *con, char *buf, int bufsize)
{
        int len = 0;
        int s = 0;
        int fd = con->fd;
      
        while (len < bufsize)
        {
                s = read(fd, &buf[len], bufsize - len);
                if(s < 0)//出错
                {
                    
                        if (errno == EAGAIN || errno == EINTR)//被中段或发送缓冲区已满
                        {
                                s = 0;
                        }
                        else
                        {
                                con->FDConnectionSetReadState(READ_DRIVE_FD_ERROR);
                                return -1; //-1出错
                        }
                       
                }
                else if (0 == s) //客户端关闭了连接
                {
                        con->is_readable = 0;
                        con->FDConnectionSetState(CON_STATE_READ);
                        con->FDConnectionSetReadState(READ_DRIVE_FD_CLOSE);
                        return -2;
                }

                len += s;
        }
}

[ 本帖最后由 qshkyo 于 2009-11-10 11:29 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP