博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
curl_multi在抓取数据中的并发实现
阅读量:7117 次
发布时间:2019-06-28

本文共 2783 字,大约阅读时间需要 9 分钟。

hot3.png

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]

转载于:https://my.oschina.net/u/586648/blog/184020

你可能感兴趣的文章
解决vue报错Failed to mount component
查看>>
活学活用! 用Local Storage实现多人聊天室
查看>>
炫酷粒子表白,双十一脱单靠它了!
查看>>
Javascript--常用方法
查看>>
[译]迁移到新的 React Context Api
查看>>
线程池你真不来了解一下吗?
查看>>
【跃迁之路】【424天】程序员高效学习方法论探索系列(实验阶段181-2018.04.05)...
查看>>
Angular学习笔记之集成三方UI框架、控件
查看>>
解决“有边框的子元素宽度设定绝对值后,缩放浏览器会错位”的两种方法
查看>>
angular 基于ng-messages的验证
查看>>
三、取get
查看>>
利用PHP实现常用的数据结构之写在前面(小白系列文章一)
查看>>
使用asprise进行图片验证码识别
查看>>
77% 的网站使用了至少有 1 个漏洞的 JavaScript 库
查看>>
一个可以提高开发效率的Git命令-- Cherry-Pick
查看>>
.NET Core 3.0中的数据库驱动框架System.Data
查看>>
数据库设计中的9大常见错误
查看>>
柔性自动化在物流的应用及探索
查看>>
微软最具价值技术专家:我的16年软件开发经验总结
查看>>
腾讯云+未来高峰对话:智能+时代的创新与探索
查看>>