HTTP协议下的IO阻塞任务,如何保证请求均匀分配到进程

whyme

比如以HTTP协议创建了5个进程,然后每个进程里的业务执行时间是2秒。

然后并发4个用户,16次请求

ab -c 4 -n 16 'http://127.0.0.1:8080/'

结果请求并没有均匀分配到进程,某个进程对某个请求进行了阻塞

2018-05-31 15:48:22.6796 service_log 'executeTime:2.0680978298187'
2018-05-31 15:48:22.6797 service_log 'executeTime:2.0688819885254'
2018-05-31 15:48:22.6799 service_log 'executeTime:2.069078207016'
2018-05-31 15:48:22.6801 service_log 'executeTime:2.0692131519318'

2018-05-31 15:48:24.6901 service_log 'executeTime:2.0080099105835'
2018-05-31 15:48:24.6901 service_log 'executeTime:2.0079879760742'

2018-05-31 15:48:26.6998 service_log 'executeTime:2.0084669589996'
2018-05-31 15:48:26.6998 service_log 'executeTime:2.0076689720154'
2018-05-31 15:48:26.6998 service_log 'executeTime:2.0074841976166'
2018-05-31 15:48:26.6999 service_log 'executeTime:2.0084929466248'

2018-05-31 15:48:28.7072 service_log 'executeTime:2.0056090354919'
2018-05-31 15:48:28.7073 service_log 'executeTime:2.0057229995728'
2018-05-31 15:48:28.7073 service_log 'executeTime:2.0057458877563'

2018-05-31 15:48:30.7168 service_log 'executeTime:2.0076150894165'
2018-05-31 15:48:30.7171 service_log 'executeTime:2.0088679790497'
2018-05-31 15:48:30.7173 service_log 'executeTime:2.0084898471832

可以通过在建立连接后暂停请求,结束后关闭请求的方式,避免多个请求阻塞到一个进程中执行:

$worker->onConnect = function(TcpConnection $connection)use($worker) {
    $worker->pauseAccept(); // 暂停接收请求

    // 接收消息回调
    $connection->onMessage = function (TcpConnection $connection, $data) {
        // 耗时代码
        $connection->close('success‘); // 关闭连接
    };
};
$worker->onClose = function(TcpConnection $connection)use($worker) {
    $worker->resumeAccept(); // 恢复接收请求
};

最后就得到了如下结果:

2018-05-31 16:34:22.2053 service_log 'executeTime:2.058121919632'
2018-05-31 16:34:22.2053 service_log 'executeTime:2.0567870140076'
2018-05-31 16:34:22.3065 service_log 'executeTime:2.1597728729248'
2018-05-31 16:34:22.3065 service_log 'executeTime:2.1576390266418'

2018-05-31 16:34:24.3266 service_log 'executeTime:2.1183500289917'
2018-05-31 16:34:24.3267 service_log 'executeTime:2.1188020706177'
2018-05-31 16:34:24.4269 service_log 'executeTime:2.1188299655914'
2018-05-31 16:34:24.4272 service_log 'executeTime:2.118989944458'

2018-05-31 16:34:26.4402 service_log 'executeTime:2.1116380691528'
2018-05-31 16:34:26.4402 service_log 'executeTime:2.1118400096893'
2018-05-31 16:34:26.5402 service_log 'executeTime:2.1115860939026'
2018-05-31 16:34:26.5402 service_log 'executeTime:2.1114778518677'

2018-05-31 16:34:28.4544 service_log 'executeTime:2.0095458030701'
2018-05-31 16:34:28.5551 service_log 'executeTime:2.1137058734894'
2018-05-31 16:34:28.6556 service_log 'executeTime:2.1138899326324'
2018-05-31 16:34:28.6556 service_log 'executeTime:2.1138758659363'

以上是单一的HTTP协议的worker模型,之后我试过用HTTP协议的worker接收请求,然后异步转发给TCP协议的worker进行处理,发现如果一次性发起多个请求时,TCP协议的worker也会出现类似上面的情况,然后通过上面的方法,也很完美的解决了这个问题。

最后我觉得pauseAccept()、resumeAccept()应用场景很多啊,比如生存消费者模型中,消费者IO阻塞等待任务,又比如对时间点要求较高的爬虫任务,能尽量的均匀分配请求到进程,进而减少时间差。

官方文档目前还没有相应描述,我还是好不容易找了一篇文章中的关键字pauseAccept,然后搜索源码发现的。。。这真是两个神奇的方法,希望官方能在后面补充一下,对于我这类的小白就帮助大了,哈哈哈

3976 3 2
3个评论

walkor

感谢你的分享。因为pauseAccept()、resumeAccept()用的比较少,确实没有列入文档,后面会加上

  • 暂无评论
whyme

好的,主要也是我的业务需求比较特殊。。。哈哈

  • 暂无评论
dignfei

牛逼!!!!!!!!!!!!!!!!!!!!

  • 暂无评论
年代过于久远,无法发表评论

whyme

2009
积分
0
获赞数
0
粉丝数
2018-05-31 加入
🔝