curl_multi在抓取数据中的并发实现

curl_multi在抓取数据中的并发实现

发表于2年前(2013-12-12 19:16)   阅读( 912) | 评论( 0)  7人收藏此文章, 我要收藏
0

9月19日成都 OSC 源创会正在报名,送机械键盘和开源无码内裤  

摘要  主要是针对curl_multi_*以及rolling-curl多并发框架核心代码的理解

h3. 背景



       在网校最初时,接入的网校就几家,而且课程数据量较小,为快速开发上线,就采取逐个遍历课程数据源,进行抓取,然后解析数据并处理,存入到数据库中。但是随着网校的接入, 且为展示更多课程数据信息,字段内容又不断调整,导致请求的数据变大,抓取的时间变得延长,顺序的处理流程,又容易造成如果某个数据源出错,导致剩下的网校课程数据无法抓取。 
      恰逢网校要开发详情页,需要调整字段,引入更多数据,抓取的当前框架有些耦合,特别是网络处理这些,不能支持多并发,担心随着数据量的增大,处理完后,会严重导致不断调整cron中的其他业务脚本,毕竟依赖最新的数据,如删除过 期的数据,向引擎推送数据等,未来是打算整合这些脚本,但提高数据抓取与处理的性能,还是必须做的。 


h3. 数据依赖与并发


      网校目前的各个数据源都是来自于各个教育机构,因而没有依赖性,这就在抓取数据时,方便并发处理;如果网校的数据源存在依赖,特别是某条数据源的请求依赖上次某条数据源的处理结果,则将不得不采用串行处理。即使并发,也可并发 请求完所有数据,然后再对数据进行处理,如图 
 


      但是会发现,这种方式并不能利用客户端等待服务端数据到达的空隙,更好的处理是,当某个数据源的数据到达时,就能立即处理,而不是等待所有的数据源的数据到达时,再逐个处理数据。在目前的网校课程数据源中,不同教育机构给出的课程 数差异很大,有些数据可能要持续两三分钟,才能请求完,而有些数据几秒内就可到达,因而谁到处理谁,能更好利用CPU。 


h3. 并发的实现



       在当前PHP并发的方法中,可以使用curl_multi_*系列的方法,也可利用其它人开发的专用并发框架,如鸟哥写的yar,考虑到学习成本与资源,就直接采用curl_multi_*方法,而且本身也有封装好的专门用于“谁到处理谁”的框架RollingCurl, 
提供更好的网络控制处理,并提供相关的DEMO,但核心代码依然是curl_multi_*系列方法的处理: 
{code:java} 
  do { 
            while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ; 
            if ($execrun != CURLM_OK) 
                break; 
            // a request was just completed -- find out which one 
            while ($done = curl_multi_info_read($master)) { 
                // get the info and content returned on the request 
                ... 


                // send the return values to the callback function. 
                ... 


                // start a new request (it's important to do this before removing the old one) 
                ... 
                } 


                // remove the curl handle that just completed 
                curl_multi_remove_handle($master, $done['handle']); 
            } 


            // Block for data in / output; error handling is done by curl_multi_exec 
            if ($running) 
                curl_multi_select($master, $this->timeout); 
        } while ($running); 
{code} 


       当调用curl_multi_add_handle添加curl句柄时,就会进入do-while大循环代码,首先执行curl_multi_exec,但是此方法,在do-while循环中被多次调用,PHP文档给此方法的解释是“处理在栈中的每一个句柄。 
无论该句柄需要读取或写入数据都可调用此方法。”,也就是说第一次调用此方法是用于发出HTTP请求数据,当栈中的句柄还有数据需要传送时,就会返回 CURLM_CALL_MULTI_PERFORM,当返回CURLM_OK只是意味着数据传送完毕或者没有数据 可传送。此方法是不阻塞的,也就是说一旦栈中某个或多个curl句柄有数据读取或者写入,就可以调用此方法传送数据,并立即返回栈中句柄的活动状态。 
       url_multi_info_read方法是检查批处理句柄中某次传送数据结束的状态(当curl_multi_exec调用返回CURLM_OK),可能上次并没有数据可传送,则队列中并没有任何消息,则就会返回false;可能传送数 
据出现错误,消息队列就会有某个批处理句柄出错的信息,当数据成功完成时,则返回的消息体中的msg字段值会为CURLMSG_DONE,其他值都不可用,表明出错。 
       如果没有后面的curl_multi_select方法上述程序也能正常运行,但是就会发现整个程序处于不停空转中,毕竟网络传输数据占用时间还是蛮长的,每次调用curl_multi_exec可能都是立即返回CURLM_OK,然后执行 
curl_multi_info_read,又立即返回false,如此继续。而调用curl_multi_select,就会等待直到多个SOCKET中的某个处于活跃状态,不然就处于阻塞状态,直到超时为止,就不会出现程序忙空转状态。 
       在上述程序代码中,可以看到,当某次传送数据完成后,就会立即处理返回来的数据,而不是待所有句柄的请求数据都完成后,才继续处理数据,其实这种等待所有请求数据都完成时再处理的情形,也有很多应用场景,如某个页面的数据, 来自于多个源,只有组装在一起,才能渲染页面。到时只需要将调用curl_multi_info_read方法的那块代码移除,在do-while循环后添加数据处理的过程即可。 


h5. 参考文献



[php curl document|http://www.php.net/manual/zh/ref.curl.php] 
[rolling-curl|https://github.com/takinbo/rolling-curl] 
[php-src|https://github.com/php/php-src] 
[libcutl-multi API document|http://curl.haxx.se/libcurl/c/libcurl-multi.html] 
[curl-src|https://github.com/bagder/curl] 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值