2017/10/09

PHP curl 的一些特性

curl 在 PHP 中是以 extension 形式存在,所以只能透過 resource reference 去操作 curl 行為,沒辦法透過 debug 工具摸清楚 curl 在背景到底做了哪些事情。

手癢用 memory_get_usage() 看了一下 curl 在 init、exec 以及 close 這幾個狀態的記憶體使用量,來猜測 curl 到底怎麼運作。

echo memory_get_usage() . "\n"   // 236136;

$ch = curl_init();
var_dump($ch);                   // resource(4) of type (curl)
echo memory_get_usage() . "\n";  // 237472

在 init_curl() 以後,會先在記憶體中 allocate 並放一些資料,所以吃掉大概 1 KB 左右的記憶體,再將 curl 的 reference 傳出來給 $ch。

接下來這邊讓 curl 去 http://assets.blog.zeroplex.tw/2017-07/php-bench-v2.html 抓一個大小 128 KB 的網頁,並看一下記憶體使用量:

echo memory_get_usage() . "\n"  // 235744;

$ch = curl_init();
echo memory_get_usage() . "\n";  // 237072

curl_setopt($ch, CURLOPT_URL, "http://assets.blog.zeroplex.tw/2017-07/php-bench-v2.html");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_exec($ch);
echo memory_get_usage() . "\n";  // 371784

curl_close($ch);
echo memory_get_usage() . "\n"  // 235928;

var_dump($ch);                  // resource(4) of type (Unknown)

由整個記憶體使用量,可以看出在 curl_exec() 之後,會把傳回來的資料除存在記憶體中,除了網頁 128KB 的內容以外,可能還包括一些與 HTTP 連線有關的資料,所以記憶體使用量增加了 130 KB (371784 - 237072 = 134712) 左右。這些資料存在於記憶體中,直到呼叫了 curl_close() 才會釋放掉。

比較有趣的是 PHP 官方文件只有提到 curl_close() 會關閉 session,但是沒有提到用來儲存 resource 的 $ch reference 會如何。上面可以看到 curl_close() 以後,其實 resource refence 還存在,只是 resource 已經被 released,所以 type 顯示為 unknown。

由此可知,沒有 unset($ch) 還拿原來的 resource reference 亂用的話應該會大爆炸 XD


最後來看一下,如果沒有做 curl_close() 就一直做 curl_exe() 抓多個網頁,到底會不會有 memory leak 問題發生:
for ($i = 0; $i < 10; $i++) {
    curl_exec($ch);
    echo memory_get_usage() . "\n";
}
執行結果如下:
369424
369976
372568
370856
372568
372568
372568
372568
370856
372568

所以看起來每次執行 curl_exec() 之前,curl 會自動將上一次的 response 清掉在重新做 request,所以記憶體不會無止境的增加。換而言之,如果今天有很複雜的 curl_setopt() 要處理,可以不用一直 reset curl 也不會有問題,setopt 只要做一次就可以了,可以省下一些時間。

原本想要測試一下 keep alive 的效果如何,但是沒有較為穩定的環境來測試,所以這邊就先跳過了。但大家可以參考 StackOverflow 上面的這篇「Persistent/keepalive HTTP with the PHP Curl library」來設定 keep alive。

沒有留言:

張貼留言