【问题标题】:What is the most efficient way to get a part of a remote XML file via PHP?通过 PHP 获取远程 XML 文件的一部分的最有效方法是什么?
【发布时间】:2014-09-02 17:17:02
【问题描述】:

我正在尝试获取远程 XML 文件的一部分,在本例中是通过 Google 地图获取邮政编码的经度和纬度。这是我目前正在使用的功能:

function slug_get_coordinates( $zip ) {
    $url = "http://maps.googleapis.com/maps/api/geocode/xml?address={$zip}&sensor=false";
    $result = simplexml_load_file( $url );
    $coordinates = $result->result->geometry->location;
    return $coordinates;
}

这有时会在长时间页面加载后起作用,但通常会超时。有没有一种方法可以直接访问我需要的 XML 文件部分,而无需加载整个文件或其他方式来优化它?

【问题讨论】:

  • 您可以使用 cURL 来检索文件。它接受timeout option。就仅检索远程文件的一部分而言,我认为这是不可能的。 HTTP 服务器根据请求提供整个文件。
  • 瓶颈是什么?连接、传输、DNS 查找等。先找出来。
  • @Jack 这是一个很好的问题。你会如何推荐测试?目前我正在 MAMP 中进行测试。
  • 当您使用cURL 时,您可以使用curl_getinfo() 来非常详细地了解哪个部分慢。

标签: php xml performance


【解决方案1】:

通过 PHP 获取远程 XML 文件的一部分最有效的方法是什么?

这个问题无法具体回答,因为它有很多含义。

实际上,您在这里处理的是远程服务。我通常建议总是包装/代理这些(比较:The Daily Mistake: Not to Proxy Remote Services)。这是因为它们总是带有许多含义。您已经通过将数据检索包装到一个函数中来部分地做到这一点:

function slug_get_coordinates( $zip ) {
    $url = "http://maps.googleapis.com/maps/api/geocode/xml?address={$zip}&sensor=false";
    $result = simplexml_load_file( $url );
    $coordinates = $result->result->geometry->location;
    return $coordinates;
}

然而,这个单一的功能不足以处理此类服务可能带来的所有问题。例如。远程位置可能刚刚关闭 - 这样的 响应超时 可能符合条件 - 或者正如您所经历的那样,它需要的时间太长了。

所以你对服务质量基本不满意

由于远程服务很常见,您无法完全控制它们并且您对服务质量的影响很小,包装它的好处是您可以在包装器中处理此类问题,因此保护您的应用程序逻辑的其余部分免受所有这些影响。

这不仅需要您封装数据的解析(就像您开始使用您拥有的功能一样),还需要封装检索(远程请求)。您还需要进行函数中缺少的错误处理。这里的另一个提示是为失败而设计。例如。即使您尝试检索(添加)的信息丢失,您的应用程序仍然可以工作。

这种代理的另一个好处是,您可以在其中实现调试功能。比如你提供的代码,我快速检索数据完全没有问题:

$zip = '55416';

$start = microtime(true);

slug_get_coordinates($zip)->asXML('php://output');

printf("\n----\nTook %.5f seconds\n", microtime(true) - $start);

输出:

<location>
    <lat>44.9465193</lat>
    <lng>-93.3439291</lng>
   </location>
----
Took 0.11873 seconds

如果您需要更深入地了解远程请求,您可以挂钩到 PHP 的流通知。我已经编译了一个StreamNotifyPrinter,它可以完成这项工作并且可以轻松注册:

$zip = '55416';

$notifier = new StreamNotifyPrinter();
libxml_set_streams_context($notifier->registerOnContext());

$start = microtime(true);

slug_get_coordinates($zip)->asXML('php://output');

printf("\n----\nTook %.5f seconds\n", microtime(true) - $start);

输出:

2014-07-12T09:07:40.146422+0000 [0.00000] Connected...
2014-07-12T09:07:40.228122+0000 [0.08170] Found the mime-type: application/xml; charset=UTF-8
2014-07-12T09:07:40.228251+0000 [0.08183] Made some progress, downloaded 0 so far
2014-07-12T09:07:40.228341+0000 [0.08192] Made some progress, downloaded 757 so far
<location>
    <lat>44.9465193</lat>
    <lng>-93.3439291</lng>
   </location>
----
Took 0.11873 seconds

如果这些信息还不够,您可能需要切换传输层(例如,使用 Curl 作为 Jack suggested,它有一个更专业的 API 用于调试请求;请参阅 Php - Debugging Curl )。

最后一招:如果您无法通过故障排除解决问题,将其正确包装可能会很容易用更好的工作数据库替换远程服务。但也许检查一下地理坐标是否真的可以很好地与邮政编码一起使用:Where can I obtain an up-to-date list of US ZIP Codes with Latitude and Longitude Geocodes?

【讨论】:

    【解决方案2】:

    除非服务提供此功能,否则您不能请求基于选择器(例如 XPath / CSS)的部分响应。

    关于效率,我建议尽可能详细地分解请求时间,以找出某个请求需要一段时间的原因。 cURL 对此有很好的支持,例如:

    $zip = urlencode('1 infinite loop');
    $ch = curl_init("http://maps.googleapis.com/maps/api/geocode/xml?address={$zip}&sensor=false");
    curl_exec($ch);
    print_r(curl_getinfo($ch));
    

    这会产生一个包含您请求的所有元数据的数组:

    Array
    (
        ...
        [total_time] => 0.11955
        [namelookup_time] => 0.02996
        [connect_time] => 0.035803
        [pretransfer_time] => 0.035874
        ...
        [size_upload] => 0
        [size_download] => 1737
        [speed_download] => 14529
        [speed_upload] => 0
        [download_content_length] => -1
        [upload_content_length] => 0
        [starttransfer_time] => 0.119444
        [redirect_time] => 0
        ...
    )
    

    利用这些数据,您可以制定适当的方法来处理问题。缓存响应可能会减少频繁重复搜索的请求时间,但它可能不适用于您的特定情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-30
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 2014-02-01
      • 1970-01-01
      相关资源
      最近更新 更多