php curl header_PHP中的yield与协程(二十一节)
大家好,我是老李。
順風(fēng)說(shuō)騷話,逆風(fēng)講道理
最近在大家一起努力下,那個(gè)沙雕肺炎患病人數(shù)增長(zhǎng)率下降了不少,總體來(lái)說(shuō)還算順,所以今天這篇注定又要騷話連篇了。聽(tīng)說(shuō)最近不少玉米開(kāi)始向大連、威海、煙臺(tái)方向涌入,算是潛在的風(fēng)險(xiǎn)吧,總之,大家也都別放松警惕,不要看到曙光時(shí)候給撂倒了。
上一節(jié)課我們說(shuō)的主要是在謝頂?shù)廊?--- 老李的提示下,你初步使用了yield...那個(gè)你沒(méi)有名字也不好,給你起個(gè)名字,洋氣點(diǎn)兒就叫歐陽(yáng)吧,一聽(tīng)就是個(gè)貴族富少繼承者們。
喊人先喊親,要把讀者叫舒心
文章別嫌少,得把客官伺候好
開(kāi)工第一天,你的老板原上草又交給你一個(gè)任務(wù)并信誓旦旦地答應(yīng)你如果你能順利完成任務(wù)那么這個(gè)月的打卡遲到費(fèi)就不扣你的了。強(qiáng)大的利好信息讓你欲罷不能。事情是這樣的,還是上一集那個(gè)內(nèi)存只有100KB的單片機(jī),這個(gè)單片機(jī)會(huì)訪問(wèn)三方服務(wù)公司的一個(gè)API,但有時(shí)候這個(gè)API會(huì)抽風(fēng),老板原上草的意思是這會(huì)兒不要阻塞等待而是讓這個(gè)單片機(jī)干點(diǎn)兒別的事兒,等API訪問(wèn)OK了再讓TA回來(lái),總之別讓TA閑著,算是免費(fèi)送這個(gè)單片機(jī)一個(gè)滿滿的福報(bào)。
考慮到昨天謝頂?shù)廊嗽?jīng)給你科普過(guò)的yield似乎擁有一種讓出CPU實(shí)現(xiàn)用戶調(diào)度的能力,你決定展現(xiàn)一波兒自我,而謝頂?shù)廊艘矝Q定用那雙充滿了老繭子的手手把手輔導(dǎo)你。
<?php function gen1() { for( $i = 1; $i <= 10; $i++ ) { echo "GEN1 : {$i}".PHP_EOL; // sleep沒(méi)啥意思,主要就是運(yùn)行時(shí)候給你一種切實(shí)的調(diào)度感,你懂么 sleep( 1 ); // 這句很關(guān)鍵,表示自己主動(dòng)讓出CPU,我不下地獄誰(shuí)下地獄 yield; }}function gen2() { for( $i = 1; $i <= 10; $i++ ) { echo "GEN2 : {$i}".PHP_EOL; // sleep沒(méi)啥意思,主要就是運(yùn)行時(shí)候給你一種切實(shí)的調(diào)度感,你懂么 sleep( 1 ); // 這句很關(guān)鍵,表示自己主動(dòng)讓出CPU,我不下地獄誰(shuí)下地獄 yield; }}$task1 = gen1();$task2 = gen2();while( true ) { // 首先我運(yùn)行task1,然后task1主動(dòng)下了地獄 echo $task1->current(); // 這會(huì)兒我可以讓task2介入進(jìn)來(lái)了 echo $task2->current(); // task1恢復(fù)中斷 $task1->next(); // task2恢復(fù)中斷 $task2->next();}GET不到發(fā)生了什么,是嗎?就是gen1()和gen2()可以交替運(yùn)行并且每次都是接著從上次的地方開(kāi)始運(yùn)行,你要用傳統(tǒng)的function是完全做不到的,傳統(tǒng)的function只能一口氣先完成其中一個(gè)函數(shù)中的for()然后再能完成另外一個(gè)function中的for(),比如下面這坨:
<?php function gen1() { for( $i = 1; $i <= 10; $i++ ) { echo "GEN1 : {$i}".PHP_EOL; sleep( 1 ); }}function gen2() { for( $i = 1; $i <= 10; $i++ ) { echo "GEN2 : {$i}".PHP_EOL; }}gen1();gen2();// 看這里,看這里,看這里!// 上面的代碼一旦運(yùn)行,一定是先運(yùn)行完gen1函數(shù)中的for循環(huán)// 其次才能運(yùn)行完gen2函數(shù)中的for循環(huán),絕對(duì)不會(huì)出現(xiàn)// gen1和gen2交叉運(yùn)行這種情況我似乎已然精通了yield
好了歐陽(yáng),讓我們展示真正的技術(shù)吧!下面這個(gè)demo,如果訪問(wèn)某個(gè)API阻塞的話就主動(dòng)讓出CPU,然后讓出的CPU開(kāi)始往一個(gè)文件里寫字符串...反正不能讓TA閑著:
<?php $ch1 = curl_init();// 這個(gè)地址中的php,我故意sleep了5秒鐘,然后輸出一坨jsoncurl_setopt( $ch1, CURLOPT_URL, "http://www.selfctrler.com/index.php/test/test1" );curl_setopt( $ch1, CURLOPT_HEADER, 0 );$mh = curl_multi_init();curl_multi_add_handle( $mh, $ch1 );//?gen1中就是調(diào)用三方API,基于multi-curl實(shí)現(xiàn)function gen1( $mh, $ch1 ) { do { $mrc = curl_multi_exec( $mh, $running ); // 請(qǐng)求發(fā)出后,讓出cpu $rs = yield;????//?生產(chǎn)環(huán)境千萬(wàn)別這么干......????// 這里加sleep是為了讓你看的更清楚流程 sleep( 1 ); echo "收到外部發(fā)送數(shù)據(jù){$rs}".PHP_EOL; } while( $running > 0 ); $ret = curl_multi_getcontent( $ch1 ); echo $ret.PHP_EOL; return false;}//?gen2是寫文件...function gen2() { for ( $i = 1; $i <= 10; $i++ ) { echo "gen2 : {$i}".PHP_EOL; file_put_contents( "./yield.log", "gen2".$i.PHP_EOL, FILE_APPEND ); $rs = yield; // 生產(chǎn)環(huán)境千萬(wàn)別這么干...... // 這里加sleep是為了讓你看的更清楚流程 sleep( 1 ); echo "收到外部發(fā)送數(shù)據(jù){$rs}".PHP_EOL; }}$gen1 = gen1( $mh, $ch1 );$gen2 = gen2();while( true ) { echo $gen1->current(); echo $gen2->current(); $gen1->send("gen1"); $gen2->send("gen2");}上面這坨代碼在飛起來(lái)后,我們?cè)俚却齝url發(fā)起請(qǐng)求的5秒鐘內(nèi),同時(shí)可以完成文件寫入功能,如果換做平時(shí)的PHP程序,就只能是先阻塞等待curl拿到結(jié)果后才能完成文件寫入,有了一絲絲內(nèi)味兒了嗎?
協(xié)程味兒
但是這里必須要值得注意的是,歐陽(yáng)在gen1()的代碼里用的并不是我們一般時(shí)候用的curl方法,而是curl_multi_exec(),為啥呢?因?yàn)橐话惆阄覀冏畛S玫腜HP curl方法都是阻塞的,這很致命,這里要點(diǎn)就是:全程不能阻塞,阻塞一處死翹翹。實(shí)際上這里最標(biāo)準(zhǔn)的用法就是curl_multi_exec()配合curl_multi_select()。所以,擴(kuò)散一下思維如果你用file_get_contents()也是不行的。
下面由謝頂?shù)廊丝偨Y(jié)一個(gè)PHP中yield的典型使用方法:如果要使用yield實(shí)現(xiàn)「異步」,實(shí)際上在PHP里也只能是結(jié)合select或epoll這些IO服用,具體就是當(dāng)IO沒(méi)有ready的時(shí)候,yield出讓CPU去做別的事情,一旦IO ready了就回來(lái)繼續(xù)執(zhí)行原來(lái)的任務(wù),說(shuō)白了就是協(xié)程調(diào)度器!
???
那TM我要這yield到底有啥用?謝頂?shù)廊四阏@幽默呢?感覺(jué)蒙娜麗莎都是你逗笑的呢~我直接用之前章節(jié)里基于libevent實(shí)現(xiàn)的服務(wù)器不就挺好用的嗎?這里要說(shuō)的就是「基于IO復(fù)用實(shí)現(xiàn)的異步非阻塞服務(wù)器中難以避免的異步回調(diào)地獄」寫法,說(shuō)白了就是一層又一層嵌套的on。這在NodeJS里頗為常見(jiàn),所以后來(lái)NodeJS出了一個(gè)叫做Promise的關(guān)鍵字來(lái)緩解這個(gè)問(wèn)題,這里你可以粗暴的認(rèn)為yield就是PHP版本的Promise,就是傳說(shuō)中的「用傳統(tǒng)同步代碼的寫法寫異步」,但也依然能寫出高IO的程序。
世面上有什么典型作品嗎?有啊,swoole呀,swoole協(xié)程就是基于epoll實(shí)現(xiàn)的協(xié)程調(diào)度器;還有微信開(kāi)源的libco也基本上是基于IO復(fù)用實(shí)現(xiàn)的協(xié)程調(diào)度器。要注意的基于epoll實(shí)現(xiàn)協(xié)程調(diào)度器只是一種實(shí)現(xiàn)方式而已,像Golang則是完全是自己在上層實(shí)現(xiàn)的調(diào)度器。
好看的皮囊就是好看,有趣的靈魂愛(ài)咋咋滴...
總結(jié)
以上是生活随笔為你收集整理的php curl header_PHP中的yield与协程(二十一节)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python中break和continu
- 下一篇: 【LeetCode笔记】283. 移动零