前言

这个是很久以前做的一个测试,当时是为了确定新项目使用什么来进行通信。之前一直用soap,但感觉性能方面不是很好。现在贴出来,顺便分享一下curl和rolling curl的代码。

上代码

CURL

function makeRequest($url, $params, $method = 'post', $protocol = 'http'){
    $query_string = '';
    if(is_array($params)){
        $query_string = http_build_query($params);
    }elseif(is_string($params)){
        $query_string = $params;
    }
    $ch = curl_init();
    if('get' == $method){
        curl_setopt($ch, CURLOPT_URL, $url.'?'.$query_string);
    }else{
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $query_string);
    }

    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);

    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); // disable 100-continue

    if('https' == $protocol){
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    }

    $ret = curl_exec($ch);
    $err = curl_error($ch);

    if(false === $ret || !empty($err)){
        $errno = curl_errno($ch);
        $info = curl_getinfo($ch);
        curl_close($ch);

        return array(
            'result' => false,
            'errno' => $errno,
            'msg' => $err,
            'info' => $info,
        );
    }

    curl_close($ch);

    return array(
        'result' => true,
        'msg' => $ret,
    );
}

$s = explode(' ', microtime());

foreach ($urls as $url) { // $urls为链接地址
    echo '<pre>';
    usleep(2000); // 设定处理返回数据的时间,即模拟结果处理
    makeRequest($url, array(), 'get');
}

$e = explode(' ', microtime());
echo ($e['1'] + $e['0']) - ($s['1'] + $s['0']);

rolling curl

class Request {
    // stores the url, method, post_data, headers and options for each request
    private $settings = array();

    function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
        $this->settings['url'] = $url;
        $this->settings['method'] = $method;
        $this->settings['post_data'] = $post_data;
        $this->settings['headers'] = $headers;
        $this->settings['options'] = $options;
    }

    public function __get($name) {
        if (isset($this->settings[$name])) {
            return $this->settings[$name];
        }
        return false;
    }

    public function __set($name, $value) {
        $this->settings[$name] = $value;
        return true;
    }

    public function __destruct() {
        unset($this->settings);
    }
}

// class that holds a rolling queue of curl requests
class RollingCurl {

    /*
    window_size is the max number of simultaneous connections allowed.
    REMEMBER TO RESPECT THE SERVERS.  sending too many requests at one time can easily be perceived as a DOS attack
    increase this window_size if you are making requests to multiple servers or have permission from the receving server admins.
    */
    private $window_size = 5;
    private $callback;

    // set your base options that you want to be used with EVERY request
    private $options = array(CURLOPT_SSL_VERIFYPEER => 0,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_FOLLOWLOCATION => 1,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_TIMEOUT => 30);
    private $headers = array();

    // the request queue
    private $requests = array();

    function __construct($callback = null) {
        $this->callback = $callback;
    }

    public function __get($name) {
        return (isset($this->{$name})) ? $this->{$name} : null;
    }

    public function __set($name, $value){
        // append the base options & headers
        if ($name == "options" || $name == "headers") {
            $this->{$name} = $this->{$name} + $value;
        } else {
            $this->{$name} = $value;
        }
        return true;
    }

    // add a request to the request queue
    public function add($request) {
        $this->requests[] = $request;
        return true;
    }

    // or alternatively
    public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
        $this->requests[] = new Request($url, $method, $post_data, $headers, $options);
        return true;
    }

    // shortcuts for get / post requests
    public function get($url, $headers = null, $options = null) {
        return $this->request($url, "GET", null, $headers, $options);
    }

    public function post($url, $post_data = null, $headers = null, $options = null) {
        return $this->request($url, "POST", $post_data, $headers, $options);
    }

    // execute the curl
    public function execute($window_size = null) {
        // rolling curl window must always be greater than 1
        if (sizeof($this->requests) == 1) {
            return $this->single_curl();
        } else {
            // start the rolling curl. window_size is the max number of simultaneous connections
            return $this->rolling_curl($window_size);
        }
    }

    private function single_curl() {
        $ch = curl_init();
        $options = $this->get_options($this->requests[0]);
        curl_setopt_array($ch,$options);
        $output = curl_exec($ch);
        $info = curl_getinfo($ch);
        // it's not neccesary to set a callback for one-off requests
        if ($this->callback) {
            $callback = $this->callback;
            $callback($output, $info);
        } else {
            return $output;
        }
    }

    private function rolling_curl($window_size = null) {
        if ($window_size)
            $this->window_size = $window_size;

        // make sure the rolling window isn't greater than the # of urls
        if (sizeof($this->requests) < $this->window_size)
            $this->window_size = sizeof($this->requests);

        // window size must be greater than 1
        if ($this->window_size < 2) {
            // TODO: add better error handling
            return "Window size must be greater than 1";
        }

        $master = curl_multi_init();
        $curl_arr = array();

        // start the first batch of requests
        for ($i = 0; $i < $this->window_size; $i++) {
            $ch = curl_init();

            $options = $this->get_options($this->requests[$i]);

            curl_setopt_array($ch,$options);
            curl_multi_add_handle($master, $ch);
        }

        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
                $info = curl_getinfo($done['handle']);
                $output = curl_multi_getcontent($done['handle']);

                // send the return values to the callback function.
                $callback = $this->callback;
                $callback($output, $info);

                // start a new request (it's important to do this before removing the old one)
                if ($i < sizeof($this->requests) && isset($this->requests[$i++]) && $i < count($this->requests)) {
                    $ch = curl_init();
                    $options = $this->get_options($this->requests[$i]);
                    curl_setopt_array($ch,$options);
                    curl_multi_add_handle($master, $ch);
                }

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

            }
        } while ($running);
        curl_multi_close($master);
        return true;
    }

    // helper function to set up a new request by setting the appropriate options
    private function get_options($request) {
        // options for this entire curl object
        $options = $this->__get('options');
        $headers = $this->__get('headers');

        // append custom options for this specific request
        if ($request->options) {
            $options = $this->__get('options') + $request->options;
        }

        // set the request URL
        $options[CURLOPT_URL] = $request->url;

        // posting data w/ this request?
        if ($request->post_data) {
            $options[CURLOPT_POST] = 1;
            $options[CURLOPT_POSTFIELDS] = $request->post_data;
        }
        if ($headers) {
            $options[CURLOPT_HEADER] = 0;
            $options[CURLOPT_HTTPHEADER] = $headers;
        }

        return $options;
    }

    public function __destruct() {
        unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
    }
}

function callback($response, $info) {
//    echo microtime().'<br>';
    usleep(2000);
    return $response;
}
$s = explode(' ', microtime());
echo '<pre>';
$rc = new RollingCurl("callback");
$rc->window_size = 100;
foreach ($urls as $url) {
    $request = new Request($url);
    $rc->add($request);
}

$rc->execute();
$e = explode(' ', microtime());
echo ($e['1'] + $e['0']) - ($s['1'] + $s['0']);

soap

详见官方

性能测试

页面个数 curl rolling curl soap
10 0.60369801521301 0.091732025146484 0.60031199455261
20 1.4378480911255 0.16679692268372 1.4413158893585
30 2.51384806633 0.16468191146851 2.5493679046631
40 3.1378140449524 0.18732190132141 3.1683931350708
50 3.473128080368 0.20288896560669 3.4912149906158

结论

curl和soap差不多,rolling_cur性能遥遥领先,且请求页面个数越多越明显。

如果您觉得您在我这里学到了新姿势,博主支持转载,姿势本身就是用来相互学习的。同时,本站文章如未注明均为 hisune 原创 请尊重劳动成果 转载请注明 转自: CURL, rolling CURL,SOAP的性能比较 - hisune.com