GotoKnow
  • เข้าระบบ
  • สมัครสมาชิก
  • แผงจัดการ
  • ออกจากระบบ
GotoKnow

PHP, Queue และ Multithread บน Windows ตอน 2

โชคดีว่า PHP 5.1.x มี function กลุ่ม CURL (หรือ libcurl พัฒนาโดยคุณ Daniel Stenberg) ซึ่งเป็น HTTP Wrapper Library สำหรับติดต่อกับ Server ผ่าน protocal ต่างๆ เช่น http, https, ftp, gopher, telnet, dict, file, and ldap protocols
  REF : PHP, Queue และ Multithread บน Windows ตอน 1 

 

เรื่องของ Multithread

ผมพัฒนา Message Queue ด้วย PHP แต่มีปัญหาอยู่ว่า บน Windows นั้น ปัจจุบัน (PHP 5.1.4) PHP ไม่สามารถ เขียน scripts แบบ multithread ได้

ยกตัวอย่าง บน Apache Daemon บน Posix ที่โดยปกติจะมีการแตก process ย่อย จำนวน 5 process เพื่อรอรับการทำงานอยู่ ซึ่งลักษณะเช่นนี้ เป็นการช่วยการทำงานด้วยการ สร้าง process แม่ แตก process ย่อย (parent process, child process)
ส่วน PHP บน Posix นั้น เราสามารถพัฒนางานเช่นนี้ได้โดยการใช้กลุ่ม Process Control Functions


แต่บน Windows นั้นไม่ support! อย่างน้อยก็ตอนนี้

อย่างไรก็ตาม โชคดีว่า PHP 5.1.x มี function กลุ่ม CURL (หรือ libcurl พัฒนาโดยคุณ Daniel Stenberg) ซึ่งเป็น HTTP Wrapper Library สำหรับติดต่อกับ Server ผ่าน protocal ต่างๆ เช่น http, https, ftp, gopher, telnet, dict, file, and ldap protocols

ซึ่ง cURL นี้สามารถเรียก http แบบ multithread ได้ :)  ลองดูตัวอย่างครับ

Listing1
php
$ch = curl_init(); // create a new curl resource
curl_setopt($ch,CURLOPT_URL, "http://www.google.co.th/"); // set URL and other appropriate options
curl_setopt($ch,CURLOPT_VERBOSE, 1);
curl_setopt($ch,CURLOPT_NOBODY, true);
curl_exec($ch); // grab URL and pass it to the browser
curl_close($ch); // close curl resource, and free up system resources
?>



Result
---------- PHP ----------
X-Powered-By: PHP/5.1.4
Content-type: text/html

* About to connect() to www.google.co.th port 80
* Trying 66.102.7.147... * connected
* Connected to www.google.co.th (66.102.7.147) port 80
> HEAD / HTTP/1.1
Host: www.google.co.th
Accept: */*

< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Cache-Control: private
< Content-Type: text/html
< Server: GWS/2.1
< Content-Length: 0
< Date: Wed, 05 Jul 2006 01:11:03 GMT
< Set-Cookie: PREF=ID=11bc0448c80c3f3a:TM=1152061863:LM=1152061863:S=j_h16U_UXFPggpBG; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.co.th
* Connection #0 to host www.google.co.th left intact
* Closing connection #0

Output completed (0 sec consumed) - Normal Termination




Listing2
<?php
$connomains = array(
"http://www.gogole.co.th/",
"http://www.yahoo.com/",
"http://www.narisa.com/",
"http://www.gotoknow.org/",
"http://www.patrickz.eu.org",
"http://www.microsoft.com"
);

$mh = curl_multi_init();
foreach ($connomains as $i => $url) {
$conn[$i]=curl_init($url);
curl_setopt($conn[$i],CURLOPT_RETURNTRANSFER,1);
curl_setopt($conn[$i],CURLOPT_VERBOSE, 1);
curl_setopt($conn[$i],CURLOPT_NOBODY, true);
curl_multi_add_handle ($mh,$conn[$i]);
}
do {
$n=curl_multi_exec($mh,$active);
} while ($active);

foreach ($connomains as $i => $url) {
$res[$i]=curl_multi_getcontent($conn[$i]);
curl_close($conn[$i]);
}
?>


Result
---------- PHP ----------
X-Powered-By: PHP/5.1.4
Content-type: text/html

* About to connect() to www.microsoft.com port 80
* Trying 207.46.225.60... * About to connect() to www.patrickz.eu.org port 80
* Trying 66.29.20.162... * About to connect() to www.narisa.com port 80
* Trying 203.150.224.210... * About to connect() to www.yahoo.com port 80
* Trying 68.142.197.75... * Connected to www.microsoft.com (207.46.225.60) port 80
> HEAD / HTTP/1.1
Host: www.microsoft.com
Accept: */*

* Connected to www.patrickz.eu.org (66.29.20.162) port 80
> HEAD / HTTP/1.1
Host: www.patrickz.eu.org
Accept: */*

* Connected to www.yahoo.com (68.142.197.75) port 80
* Connected to www.narisa.com (203.150.224.210) port 80
> HEAD / HTTP/1.1
Host: www.yahoo.com
Accept: */*

> HEAD / HTTP/1.1
Host: www.narisa.com
Accept: */*

< HTTP/1.1 200 OK
< Date: Wed, 05 Jul 2006 01:13:57 GMT
< Server: Apache/1.3.36 (Unix) mod_auth_passthrough/1.8 mod_log_bytes/1.2 mod_bwlimited/1.4 FrontPage/5.0.2.2635.SR1.2 mod_ssl/2.8.27 OpenSSL/0.9.7a PHP-CGI/0.1b
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Expires: Mon, 26 Jul 1997 05:00:00 GMT
< Pragma: no-cache
< X-Powered-By: PHP/4.4.2
< Set-Cookie: sessioncookie=7e82de586f1758f762340e481b2c38d2; expires=Wednesday, 05-Jul-06 13:13:57 GMT; path=/
< Set-Cookie: mosvisitor=1
< Last-Modified: Wed, 05 Jul 2006 01:13:57 GMT
< Content-Type: text/html
* Connection #0 to host www.narisa.com left intact
* About to connect() to www.gotoknow.org port 80
* Trying 69.59.149.4... * Connected to www.gotoknow.org (69.59.149.4) port 80
> HEAD / HTTP/1.1
Host: www.gotoknow.org
Accept: */*

< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Date: Wed, 05 Jul 2006 01:13:59 GMT
< Server: Microsoft-IIS/6.0
< P3P: CP="ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI"
< X-Powered-By: ASP.NET
< X-AspNet-Version: 2.0.50727
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
< Content-Length: 31103
* Connection #0 to host www.microsoft.com left intact
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Date: Wed, 05 Jul 2006 01:13:59 GMT
< P3P: policyref="http://p3p.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"
< Cache-Control: private
< Vary: User-Agent
< Content-Type: text/html
< Set-Cookie: FPB=at106nr5r12am4in; expires=Thu, 01 Jun 2006 19:00:00 GMT; path=/; domain=www.yahoo.com
* Connection #0 to host www.yahoo.com left intact
< HTTP/1.1 302
< Connection: Keep-Alive
< Date: Wed, 05 Jul 2006 01:15:30 GMT
< Server: Apache/1.3.36 (Unix) mod_auth_passthrough/1.8 mod_log_bytes/1.2 mod_bwlimited/1.4 FrontPage/5.0.2.2635.SR1.2 mod_ssl/2.8.27 OpenSSL/0.9.7a PHP-CGI/0.1b
< X-Powered-By: PHP/4.4.2
< Location: /index.php?load=Home
< Content-Type: text/html
* Connection #0 to host www.patrickz.eu.org left intact
< HTTP/1.1 302 Found
< Connection: Keep-Alive
< Content-Type: text/html
< Cache-Control: no-cache
< location: http://gotoknow.org/
< Content-Length: 0
< Date: Wed, 05 Jul 2006 01:14:34 GMT
< Server: lighttpd/1.4.11
< Set-Cookie: _session_id=9a01d19210e1b0a1472a5e53837f722d; path=/
* Connection #0 to host www.gotoknow.org left intact
* Closing connection #0
* Closing connection #0
* Closing connection #0
* Closing connection #0
* Closing connection #0
* Closing connection #0

Output completed (12 sec consumed) - Normal Termination


*** จะสังเกตุว่า เมื่อมีการใช้ curl_multi_exec นั้น ลำดับการติดต่อ จาก web server ไม่ได้เรียงกัน ขึ้นอยู่กับ network และความเร็วของ server ปลายทางในขณะนั้น

ทดสอบความเร็ว

ระหว่าง curl_exec และ curl_multi_exec จะเร็วสักแค่ไหน
ผมทดสอบโดยกำหนดให้ไปเปิดเว็บจำนวน 20 เว็บ วนซ้ำ 5 รอบ (100 เว็บ) ดังนี้

$connomains = array(
"http://www.gogole.co.th/",
"http://www.yahoo.com/",
"http://www.narisa.com/",
"http://gotoknow.org/",
"http://www.thaidev.com/",
"http://www.sun.com/",
"http://www.mthai.com/",
"http://www.sanook.com/",
"http://www.mcot.net/",
"http://www.cnn.com/",
"http://www.kapook.com/",
"http://www.pantip.com/",
"http://www.php.net/",
"http://www.mysql.com/",
"http://www.zend.com/",
"http://www.phpnuke.org/",
"http://www.onlamp.com/",
"http://www.nectec.or.th",
"http://www.phpfreaks.com",
"http://www.phpdeveloper.org"
);


และใช้ scripts ด้านล่างนี้ สำหรับจับเวลา (timer)
set_time_limit(0);
$stimer = explode( ' ', microtime() );
$stimer = $stimer[1] + $stimer[0];
...
...
$etimer = explode( ' ', microtime() );
$etimer = $etimer[1] + $etimer[0];
printf( "Script timer: %f seconds.", ($etimer-$stimer) );


ผลการทดสอบ
curl_exec (100 urls) : Script timer: 144.065724 seconds. (เฉลี่ย 1.44 วินาที / url)
curl_multi_exec (100 urls) : Script timer: 44.986281 seconds. (เฉลี่ย 0.45 วินาที / url) เร็วกว่า 3 เท่า

อย่างไรก็ตาม ผมทดสอบที่ 200 urls ไม่สำเร็จ เพราะทำงานได้ช้ามากๆ อาจเป็นเพราะ scripts ได้ใช้ network resource จนเต็ม (ลองคิดถึงการเปิดเว็บดูพร้อมกัน เป็นร้อยๆเว็บ พร้อมกัน)

เพื่อให้แน่ใจ ผมจึงทดสอบอีกครั้ง แต่คราวนี้เป็น ทดสอบกับ web server ในเครือข่ายของผมเอง คราวนี้ ได้ทดสอบจำนวน 500 request.
curl_exec (500 urls) : Script timer: 4.578352 seconds. (เฉลี่ย 0.009 วินาที / url)
curl_multi_exec (500 urls) : Script timer: 18.765108 seconds!!! (0.037 วินาที /url)

คราวนี้... curl_multi_exec ทำงาน ช้ากว่า curl_exec?? เพราะอะไร? ผมยังไม่ทราบแน่ชัดเช่นกัน น่าจะมีหลายสาเหตุ
- อาจเป็นเพราะเป็นการทำงานพร้อมกัน เปิด thread มากเกินไป
- web server ตอบสนอง HTTP request ไม่ทัน
- scripts ทำงานช้าเอง หรืออาจเป็นข้อจำกัดของ cURL ใน PHP ที่อาจมีการกำจัดจำนวนของ thread ในขณะหนึ่ง
- เกิด dead lock ระหว่างแต่ล่ะ thread



คุณ Corey Ward ได้ทดสอบเปรียบเทียบระหว่าง curl และ file_get_contents() ของ PHP พบว่า
cURL เร็วกว่าหลายเท่า

Calculating 10 queries to http://www.flickr.com/
..........cURL took 0.626020 seconds.
..........file_get_contents() took 1.661699 seconds.

Calculating 10 queries to http://www.yahoo.com/
..........cURL took 1.149110 seconds.
..........file_get_contents() took 2.332913 seconds.

Calculating 10 queries to http://www.ebay.com/
..........cURL took 2.041446 seconds.
..........file_get_contents() took 3.101250 seconds.

Calculating 10 queries to http://www.godaddy.com/
..........cURL took 0.330688 seconds.
..........file_get_contents() took 7.861495 seconds.

Calculating 10 queries to http://www.php.net/
..........cURL took 1.015212 seconds.
..........file_get_contents() took 2.158154 seconds.

Calculating 10 queries to http://www.hotels.com/
..........cURL took 2.529691 seconds.
..........file_get_contents() took 3.625414 seconds.

Calculating 10 queries to http://www.msn.com/
..........cURL took 0.908087 seconds.
..........file_get_contents() took 1.761305 seconds.

Calculating 10 queries to http://www.time.com/
..........cURL took 1.433099 seconds.
..........file_get_contents() took 6.109291 seconds.

Calculating 10 queries to http://www.microsoft.com/
..........cURL took 0.469168 seconds.
..........file_get_contents() took 2.668108 seconds.

Calculating 10 queries to http://www.bbb.org/
..........cURL took 0.973232 seconds.
..........file_get_contents() took 4.615307 seconds.

Calculating 10 queries to http://www.altavista.com/
..........cURL took 0.562594 seconds.
..........file_get_contents() took 1.659492 seconds.

Calculating 10 queries to http://www.excite.com/
..........cURL took 4.912833 seconds.
..........file_get_contents() took 5.566581 seconds.

Calculating 10 queries to http://www.alltheweb.com/
..........cURL took 0.509357 seconds.
..........file_get_contents() took 1.644844 seconds.

Calculating 10 queries to http://www.dogpile.com/
..........cURL took 1.268339 seconds.
..........file_get_contents() took 2.604692 seconds.

Calculating 10 queries to http://www.ask.com/
..........cURL took 3.967779 seconds.
..........file_get_contents() took 4.799190 seconds.

Calculating 10 queries to http://www.alexa.com/
..........cURL took 3.701434 seconds.
..........file_get_contents() took 2.219473 seconds.

Calculating 10 queries to http://www.amazon.com/
..........cURL took 1.316454 seconds.
..........file_get_contents() took 20.018788 seconds.

Calculating 10 queries to http://www.cnn.com/
..........cURL took 1.999775 seconds.
..........file_get_contents() took 4.435808 seconds.

Calculating 10 queries to http://www.foxnews.com/
..........cURL took 0.475676 seconds.
..........file_get_contents() took 0.848488 seconds.

Calculating 10 queries to http://www.wired.com/
..........cURL took 4.638048 seconds.
..........file_get_contents() took 4.908314 seconds.

STATS:

Total CURL Time: 34.828042 seconds.
Total FGC Time: 84.600606 seconds.
CURL was 41.17% faster.
Average CURL query time was 0.17414021 seconds.
Average FGC query time was 0.42300303 seconds.
There were 200 queries done on 20 websites to test this.

Script by Corey Ward


ลองทดสอบได้ที่ http://monitor.trucex.com/curltest.php

สรุปว่า cURL เป็น wrapper library ที่มีประสิทธิภาพสูงมากตัวหนึ่ง มีความเร็วสูง ทำงานแบบ multithread ได้ มีทั้งแบบ command line (binary) และ library(libcurl) 

 

บันทึกนี้เขียนที่ GotoKnow โดย 

คำสำคัญ (keywords): phpcurlthreadmultithreadlibcurl
หมายเลขบันทึก: 37185
เขียน:
แก้ไข:
อ่าน:
สัญญาอนุญาต: สงวนสิทธิ์ทุกประการ

ความเห็น (0)