使用PHP的API请求加载速度慢


API requests with PHP slow loading time

我使用PHP从几个比特币交易所获取价格数据和交易量数据,但是当您加载页面时需要接近20秒。我怎样才能使加载时间更好?我想这和卷发有关。

 <?php
    function getData($url) {
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $rawData = curl_exec($curl);
    curl_close($curl);
    return json_decode($rawData, true);
    }
    //BTC Volume LocalBitcoins
    $BTCVolumeLocal = getData('https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/');
    $LocalVolume = $BTCVolumeLocal["USD"]["volume_btc"];
    //BTC Volume BTCE
    $BTCVolumeBTCE = getData('https://btc-e.com/api/3/ticker/btc_usd');
    $BTCEVolume = $BTCVolumeBTCE["btc_usd"]["vol_cur"];
    //BTC Volume Bitstamp
    $BTCVolumeStamp = getData('https://www.bitstamp.net/api/ticker/');
    $StampVolume = $BTCVolumeStamp["volume"];
    //BTC Volume Bitfinex
    $BTCVolumeFinex = getData('https://api.bitfinex.com/v1/pubticker/btcusd');
    $FinexVolume = $BTCVolumeFinex["volume"];
    //BTC Volume OKCoin
    $BTCVolumeOK = getData('https://www.okcoin.com/api/ticker.do?ok=1');
    $OKCoinVolume = $BTCVolumeOK["ticker"]["vol"];
    //BTC Volume LakeBTC
    $BTCVolumeLake = getData('https://www.lakebtc.com/api_v1/ticker');
    $LakeVolume = $BTCVolumeLake["USD"]["volume"];
    //Totals the Volumes
    $TotalVolume = $LakeVolume + $FinexVolume + $OKCoinVolume + $StampVolume + $BTCEVolume + $LocalVolume;
    //Percents of Total Volume
    $BTCEPercent = $BTCEVolume / $TotalVolume;
    $StampPercent = $StampVolume / $TotalVolume;
    $FinexPercent = $FinexVolume / $TotalVolume;
    $OKPercent = $OKCoinVolume / $TotalVolume;
    $LakePercent = $LakeVolume / $TotalVolume;
    $LocalPercent = $LocalVolume / $TotalVolume;
    //BTC Price BTCE
    $BTCPriceBTCE = getData('https://btc-e.com/api/3/ticker/btc_usd');
    $BTCEPrice = $BTCPriceBTCE["btc_usd"]["last"];
    //BTC Price Bitstamp
    $BTCPriceStamp = getData('https://www.bitstamp.net/api/ticker/');
    $StampPrice = $BTCPriceStamp["last"];
    //BTC Price Bitfinex
    $BTCPriceFinex = getData('https://api.bitfinex.com/v1/pubticker/btcusd');
    $FinexPrice = $BTCPriceFinex["last_price"];
    //BTC Price OKCoin
    $BTCPriceOK = getData('https://www.okcoin.com/api/ticker.do?ok=1');
    $OKPrice = $BTCPriceOK["ticker"]["last"];
    //BTC Price LakeBTC
    $BTCPriceLake = getData('https://www.lakebtc.com/api_v1/ticker');
    $LakePrice = $BTCPriceLake["USD"]["last"];
    //BTC Price LocalBitcoins
    $BTCPriceLocal = getData('https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/');
    $LocalPrice = $BTCPriceLocal["USD"]["avg_1h"];
    //BTC Price * Percent
    $BTCEPricePercent = $BTCEPrice * $BTCEPercent;
    $StampPricePercent = $StampPrice * $StampPercent;
    $FinexPricePercent = $FinexPrice * $FinexPercent;
    $OKPricePercent = $OKPrice * $OKPercent;
    $LakePricePercent = $LakePrice * $LakePercent;
    $LocalPricePercent = $LocalPrice * $LocalPercent;
    //Bitcoin Price
    $bitcoinPrice = round($LakePricePercent + $OKPricePercent + $FinexPricePercent + $StampPricePercent + $BTCEPricePercent + $LocalPricePercent, 2);
    ?>

如果api响应不够快,而且它们不在你的控制之下,你可能无法改变这一点。

并行查询它们可能会加快速度,但正如在评论中提到的,使用PHP通常不那么容易。

如果只是关于页面的加载时间,您可以缓存API查询的结果:

    将API请求放在cronjob中,每隔x分钟自动调用一次。根据输入数据的变化速度,这可以低至1分钟(不幸的是,这意味着您将创建大量流量),或者您可以每小时查询一次,如果这对您的计算是可以的。
  • 将结果保存到本地数据库或缓存(MySQL, Redis, Memcache,…)
  • 对于您的计算,只从您的本地副本读取值,这将比每次查询服务快得多

你有12个服务电话,20秒是真实的。我不熟悉你的应用程序,但你可以考虑实现一些缓存(保存下载的数据到文件,以后从下载的文件加载)。

$url = 'http://www.example.com';
$content = '';
$file_name = 'tmp/cache.txt';
if(file_exists($file_name))){
    $content = file_get_contents($file_name);
}else{
    $content = file_get_contents($url);
    //or $content = getData($url);
    file_put_contents($file_name, $content);
}

function getData($url) {
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $rawData = curl_exec($curl);
    curl_close($curl);
    return json_decode($rawData, true);
}

我发现使用ajax,所有结果都相当快地返回,因为请求是异步运行的,到目前为止,这是正在进行的工作.....如果这里的其他人能看到我认为我从这种方法中感受到的好处,毫无疑问他们会成功的。

这个想法是一个js函数向php脚本发送一系列ajax请求,然后将curl请求发送到给定的各种比特币url。每个请求都设置为携带要从请求返回的字段- php脚本然后深入研究数据并找到该信息并返回给js。

计算各种百分比仍然是一个问题,因为您不一定知道所有请求何时完成。我猜promises可能有用?

当然,使用ajax可以让页面快速加载-然后结果返回'as和when'直到完成.....

bitcoin.php
-----------
<?php
    if( $_SERVER['REQUEST_METHOD']=='POST' ){
        $cacert='c:'wwwroot'cacert.pem';

        /* Two of the three params sent via ajax */
        $url=$_POST['url'];
        $fields=$_POST['fields'];
        /* $fields might be a comma separated list of fields, or simply one - we want an array */
        $params=( !empty( $fields ) && strstr( $fields, ',' ) ) ? explode( ',', $fields ) : (array)$fields;

        $curl=curl_init( $url );
        if( parse_url( $url, PHP_URL_SCHEME )=='https' ){
            curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, FALSE );
            curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, 2 );
            curl_setopt( $curl, CURLOPT_CAINFO, realpath( $cacert ) );
        }
        curl_setopt( $curl, CURLOPT_URL, $url );
        curl_setopt( $curl, CURLOPT_HEADER, false );
        curl_setopt( $curl, CURLOPT_FRESH_CONNECT, true );
        curl_setopt( $curl, CURLOPT_FORBID_REUSE, true );
        curl_setopt( $curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
        curl_setopt( $curl, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_OLDEST );
        curl_setopt( $curl, CURLOPT_BINARYTRANSFER, true );
        curl_setopt( $curl, CURLOPT_AUTOREFERER, true );
        curl_setopt( $curl, CURLOPT_CONNECTTIMEOUT, 10 );
        curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
        curl_setopt( $curl, CURLOPT_USERAGENT, 'Bitcoin Fuzzer' );

        $data=trim( curl_exec( $curl ) );
        $status=curl_getinfo( $curl,CURLINFO_HTTP_CODE );
        curl_close( $curl );

        /* Deal with the response data */
        if( $status==200 ){
            $json=json_decode( $data );
            /* 
                Hack: 
                there must be a better way to drill down dynamically through
                an object structure but I was too tired to think straight
                This simply echos back the response for javascript to deal with
                but I guess one could make use of sessions to do the calculations
                in php rather than js
            */
            switch( count( $params ) ){
                case 1:
                    $p1=$params[0];
                    print_r( $json->$p1 );
                break;
                case 2:
                    $p1=$params[0];
                    $p2=$params[1];
                    print_r( $json->$p1->$p2 );
                break;
                case 3:
                    $p1=$params[0];
                    $p2=$params[1];
                    $p3=$params[2];
                    print_r( $json->$p1->$p2->$p3 );
                break;
            }
        }
    }
?>

HTML页眉:

<script type='text/javascript' charset='utf-8'>
    /* simple ajax function */
    function _ajax( url, options ){
        var req=new XMLHttpRequest();
        var callback=options.hasOwnProperty('callback') ? options.callback : false;
        if( !callback ) return false;

        var headers={
            'Accept': "text/html, application/xml, application/json, text/javascript, "+"*"+"/"+"*"+"; charset=utf-8",
            'Content-type': 'application/x-www-form-urlencoded',
            'X-Requested-With': 'XMLHttpRequest'
        };
        var params=[];
        if( options.hasOwnProperty('params') && typeof( options.params )=='object' ){
            for( var n in options.params ) params.push( n + '=' + options.params[n] );
        }
        var args=options.hasOwnProperty('args') ? options.args : options;
        req.onreadystatechange=function(){
            if( req.readyState==4 ) {
               if( req.status==200 ) options.callback.call( this, req.response, args );
               else console.warn( 'Error: '+req.status+' status code returned' );
            }
        }
        req.open( 'POST', url, true );
        for( header in headers ) req.setRequestHeader( header, headers[ header ] );
        req.send( params.join('&') );
    }
    var volumes=[];
    var prices=[];
    var tot_vols=0;
    var tot_prices=0;
    function bitcoin(){
        var btc={
            vols:{
                'https:'/'/localbitcoins.com'/bitcoinaverage'/ticker-all-currencies'/':['USD','volume_btc'],
                'https:'/'/btc-e.com'/api'/3'/ticker'/btc_usd':['btc_usd','vol_cur'],
                'https:'/'/www.bitstamp.net'/api'/ticker'/':['volume'],
                'https:'/'/api.bitfinex.com'/v1'/pubticker'/btcusd':['volume'],
                'https:'/'/www.okcoin.com'/api'/ticker.do?ok=1':['ticker','vol'],
                'https:'/'/www.lakebtc.com'/api_v1'/ticker':['USD','volume']
            },
            prices:{
                'https:'/'/btc-e.com'/api'/3'/ticker'/btc_usd':['btc_usd','last'],
                'https:'/'/www.bitstamp.net'/api'/ticker'/':['last'],
                'https:'/'/api.bitfinex.com'/v1'/pubticker'/btcusd':['last_price'],
                'https:'/'/www.okcoin.com'/api'/ticker.do?ok=1':['ticker','last'],
                'https:'/'/www.lakebtc.com'/api_v1'/ticker':['USD','last'],
                'https:'/'/localbitcoins.com'/bitcoinaverage'/ticker-all-currencies'/':['USD','avg_1h']
            }
        };
        var url;
        var vols=btc.vols;
        var prices=btc.prices;
        for( url in vols ){
            getbitcoin.call( this, url, vols[url], 'volumes' );
        }
        for( url in prices ){
            getbitcoin.call( this, url, vols[url], 'prices' );  
        }
    }

    function getbitcoin( url, fields, type ){
        var options={
            callback:cbbtc,
            method:'POST',
            params:{
                'fields':fields,
                'type':type,
                'url':url
            }
        };
        _ajax.call( this, '/test/bitcoin.php', options );
    }
    function cbbtc(r,o){
        switch( o.params.type ){
            case 'volumes':
                tot_vols += parseFloat( r );
                volumes.push( parseFloat( r ) );
                document.getElementById('btc_vols').value=tot_vols;
            break;
            case 'prices':
                tot_prices += parseFloat( r );
                prices.push( parseFloat( r ) );
                document.getElementById('btc_prices').value=tot_prices;
            break;  
        }
    }           
    /* launch the function when the page is ready */
    document.addEventListener( 'DOMContentLoaded', bitcoin, false );
</script>

html page - body:

/* These simply receive values for testing */
<input type='text' id='btc_vols' />
<input type='text' id='btc_prices' />

多线程和/或缓存,朋友

PHP中的多线程并不比其他语言更难。你可以在这里找到说明:如何在PHP应用程序中使用多线程

你可以用Memcache或Redis使用简单的缓存,网上有各种各样的教程和帮助使用这些来缓存PHP的东西。