Facebook Graph API缓存JSON响应

14
我正在使用Facebook Graph API从Facebook粉丝页面获取内容,然后将其显示到网站上。 我是这样做的,并且它起作用了,但不知何故,我的托管提供商似乎会限制我的请求次数...因此,我想缓存响应并仅在例如每8小时请求一次新请求。
$data = get_data("https://graph.facebook.com/12345678/posts?access_token=1111112222233333&limit=20&fields=full_picture,link,message,likes,comments&date_format=U");
$result = json_decode($data);

get_data 函数使用 CURL 如下:

function get_data($url) {
    $ch = curl_init();
    $timeout = 5;
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $datos = curl_exec($ch);
    curl_close($ch);
    return $datos;
}

这个功能很好,我可以输出JSON数据响应并将其用于我的网站以显示内容。但是正如我所提到的,在我的主机中,这似乎每隔X时间就会失败,我猜是因为我受到了限制。我尝试使用我在Stackoverflow上看到的一些代码缓存响应。但我无法弄清如何集成和使用两个代码。我已成功创建缓存文件,但我无法正确地从缓存文件中读取并避免向Facebook Graph API发出新请求。

// cache files are created like cache/abcdef123456...
    $cacheFile = 'cache' . DIRECTORY_SEPARATOR . md5($url);

    if (file_exists($cacheFile)) {
        $fh = fopen($cacheFile, 'r');
        $cacheTime = trim(fgets($fh));

        // if data was cached recently, return cached data
        if ($cacheTime > strtotime('-60 minutes')) {
            return fread($fh);
        }

        // else delete cache file
        fclose($fh);
        unlink($cacheFile);
    }

$fh = fopen($cacheFile, 'w');
    fwrite($fh, time() . "\n");
    fwrite($fh, $json);
    fclose($fh);

return $json;

非常感谢您的帮助!


过早的优化是万恶之源。你有证据表明你的托管提供商限制了你的出站连接吗(你是否在共享托管上)?也许只需询问会更容易?Facebook也可能限制您的请求,但在这种情况下,他们会在响应中指出。每隔x次,$result的值会改变,导致您的代码无法处理它,因为缺少您期望的属性,这种情况是否发生过? - Bailey Parker
1
我建议使用像Mohammed Asif所建议的memcache(d),这是管理这些东西的一种很好的干净的方式。 - Ludo - Off the record
@PhpMyCoder 不,我不确定,但它总是在本地主机上工作,而不是在我的免费托管上,所以我非常确定他们是限制的原因。此外,Facebook 的响应没有给出任何错误。我只是得到类似于超过最大请求时间的东西...那时,我在本地主机上尝试,完全没有问题 :) - qalbiol
@qalbiol,听起来你的问题是你正在使用免费的主机(可能与许多其他站点共享IP)。如果其中任何一个站点也向Facebook的API发出请求,我想你们所有人都会一起被限制速率(FB可能会进行每个IP的限制)。 - Bailey Parker
4个回答

5

在构建缓存和缓存实际对象(甚至数组)时,有一些东西可能会派上用场。

函数serializeunserialize允许您获取对象或数组的字符串表示形式,以便将其作为纯文本进行缓存,然后从字符串中弹出对象/数组,就像之前一样。

filectime允许您获取文件的最后修改日期,因此当文件被创建时,您可以依靠此信息来查看您尝试实现的缓存是否已过期。

整个工作代码如下:

function get_data($url) {
    /** @var $cache_file is path/to/the/cache/file/based/on/md5/url */
    $cache_file = 'cache' . DIRECTORY_SEPARATOR . md5($url);
    if(file_exists($cache_file)){
        /** 
         * Using the last modification date of the cache file to check its validity 
         */
        if(filectime($cache_file) < strtotime('-60 minutes')){
            unlink($cache_file);
        } else {
            echo 'TRACE -- REMOVE ME -- out of cache';
            /** 
             * unserializing the object on the cache file 
             * so it gets is original "shape" : object, array, ...  
             */
            return unserialize(file_get_contents($cache_file));
        }
    }

    $ch = curl_init();
    $timeout = 5;
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $data = curl_exec($ch);
    curl_close($ch);

    /** 
     * We actually did the curl call so we need to (re)create the cache file 
     * with the string representation of our curl return we got from serialize 
     */
    file_put_contents($cache_file, serialize($data));

    return $data;
}

注意:请注意,我已经将您实际函数get_data中的变量$datos更改为更常见的$data


感谢@b.enoit.be的帮助,我已经成功解决了与我的托管提供商相关的问题。祝好,E. - qalbiol

3
这个答案会给你的项目添加一些依赖,但是与自己编写相关的内容相比,这可能更值得一试。
你可以使用Guzzle HTTP客户端,并结合HTTP Cache插件。
$client = new Client('http://www.test.com/');

$cachePlugin = new CachePlugin(array(
    'storage' => new DefaultCacheStorage(
        new DoctrineCacheAdapter(
            new FilesystemCache('/path/to/cache/files')
        )
    )
));

$client->addSubscriber($cachePlugin);

$request = $client->get('https://graph.facebook.com/12345678/posts?access_token=1111112222233333&limit=20&fields=full_picture,link,message,likes,comments&date_format=U');
$request->getParams()->set('cache.override_ttl', 3600*8); // 8hrs

$data = $request->send()->getBody();
$result = json_decode($data);

1

不确定您是否可以使用Memcache,如果可以:

$cacheFile = 'cache' . DIRECTORY_SEPARATOR . md5($url);
$mem = new Memcached();
$mem->addServer("127.0.0.1", 11211);
$cached = $mem->get($cacheFile);
if($cached){
  return $cached;
}
else{
  $data = get_data($url);
  $mem->set($cacheFile, json_encode($data), time() + 60*10); //10 min
}

1
如果您的托管提供商将所有出站请求都通过代理服务器推送 - 您可以尝试在请求开头附加一个额外的参数来打败它:
https://graph.facebook.com/12345678/posts?p=(randomstring)&access_token=1111112222233333&limit=20&fields=full_picture,link,message,likes,comments&date_format=U

我已成功地将其用于对第三方数据提供商的呼出电话。当然,我不知道你的问题是否是这个问题。您也可能会受到提供程序的影响,如果他们拒绝使用他们不期望的参数进行请求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接