美文网首页
给handy网络库增加线程池处理任务能力

给handy网络库增加线程池处理任务能力

作者: bingtaoli | 来源:发表于2020-05-05 18:03 被阅读0次

    handy网络库是个按照Reactor模式设计的库,在github(https://github.com/yedf/handy)上有2.9k个点赞,说明大家对这个库的实现还是很认可的。笔者也反复读过这个库的代码。

    这个库没有像muduo一样使用boost,而是完全用c++11的特性去实现了函数指针、多线程等网络库的必备,所以对于了解c++11也是一个好的入门指南。

    我上个星期开始抽出了一点业余时间来体验了下这个库,主要是这个库自带的example例子,最简单的像echo之类的。

    在跑例子的时候,我发现了handy的EventBase即事件分发器是单线程处理请求的,这就导致可能有些请求比较慢,就会阻塞后续的请求相应。其实标准的Reactor处理逻辑应该是主线程负责read接受客户端请求,然后把请求内容分发到工作线程池中去处理,这样能最大化利用服务器效率,不会阻塞请求。

    void PollerKqueue::loop_once(int waitMs) {
        //..
        lastActive_ = kevent(fd_, NULL, 0, activeEvs_, kMaxEvents, &timeout);
        while (--lastActive_ >= 0) {
            //..
            if (!(ke.flags & EV_EOF) || ch->readEnabled()) {
                ch->handleRead();
            }
        }
    }
    

    上面的代码就是会先拉取事件,然后就处理read事件,在handleRead函数中会read套接字然后回调该channel的回调函数。
    ch->handleRead()里会执行如下,简化如下:

    void TcpConn::handleRead(const TcpConnPtr& con) {
        //1、read socket
        rd = readImp(channel_->fd(), input_.end(), input_.space());
        //2、收完包后处理
        if (readcb_ && input_.size()) {
            readcb_(con);
        }
    }
    

    这整个流程是在poller主线程去实现的,只要有一个请求耗时很久,后面的请求根本无法响应。

    举个例子。修改自带的例子:Example/echo.cc,如果接受客户端请求字段是hello,就会sleep10秒,这就阻塞了dispatcher的分发能力。在阻塞的这段时间内,开启另一个终端去telnet连接服务,发送任何数据都不会有响应,因为主线程分发被阻塞了。

    svr->onConnRead([](const TcpConnPtr& con) {
        Buffer buf = con->getInput();
        if (0 == strncmp(buf.data(), "hello", 5))
        {
            trace("some request block the loop");
            sleep(10);
        }
        con->send(con->getInput());
    });
    

    那为什么不read完数据后分发到工作线程中处理呢。

    void TcpConn::handleRead(const TcpConnPtr& con) {
        //1、read socket
        rd = readImp(channel_->fd(), input_.end(), input_.space());
        //2、收完包后处理
        if (readcb_ && input_.size()) {
            //3、丢到线程池中处理
            base_->thread_pool_.addTask([=]{
                readcb_(con);
            });
        }
    }
    

    这样调整后,请求之间不会互相干扰,因为它们都是被分派给不同的线程去做了。当然如果所有的请求都要阻塞很久,那么开10个线程池就是杯水车薪了,但是总比单线程跑好得多了。

    代码调整很小,因为handy实现的线程池已经很好了,我只是稍微调整了下而已。

    相关文章

      网友评论

          本文标题:给handy网络库增加线程池处理任务能力

          本文链接:https://www.haomeiwen.com/subject/fwqvghtx.html