久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      了解PHP yield的高級用法

      了解PHP yield的高級用法

      開篇

      剛開始接觸PHPyield 的時候,感覺,yield 是什么黑科技,百度一下:yield——協(xié)程,生成器。很多文章都在講 IteratorGenerater, 蛤~,這東西是 PHP 迭代器的一個補充。再翻幾頁,就是Go 協(xié)程。我出于好奇點開看了下Go 協(xié)程, 里面都是 并發(fā),線程,管道通訊這類字眼,wc,nb, 這tm才是黑科技啊,再回來看PHP,分分鐘想轉(zhuǎn) Go。

      相關學習推薦:PHP編程從入門到精通

      yield 語法加入 PHP

      yield語法是在版本5.5加入PHP的,配合迭代器使用,功能上就是 流程控制 代碼,和goto,return 類似。

      以下就是官方提供的 yield 小例子,通過執(zhí)行結(jié)果,我們可分析當代碼執(zhí)行到 yield $i 時,他會進行 return $i, 待 echo "$valuen" 后, goto for ($i = 1; $i <= 3; $i++) {, 對!PHP 的 yield 就是一個能出能進的語法。在z代碼中七進七出,把 $i 平平安安得送了出來。

      <?phpfunction gen_one_to_three() {     for ($i = 1; $i <= 7; $i++) {         //注意變量$i的值在不同的yield之間是保持傳遞的。         yield $i;     }}$generator = gen_one_to_three();foreach ($generator as $value) {     echo "$valuen";}// output12...67

      我們遇到了什么問題

      寫代碼就是解決問題。我們來看看他們遇到了什么問題:php官方呢,需要言簡意賅地把yield介紹給大家。一部分網(wǎng)友呢,需要在有限的資源內(nèi)完成大文件操作。而我們的鳥哥。面對的一群對當下yield的教程停留于初級而不滿意的phper,就以一個任務調(diào)度器作為例子,給大家講了一種yield高級用法。

      php.net:生成器語法,
      PHP如何讀取大文件,
      風雪之隅:在PHP中使用協(xié)程實現(xiàn)多任務調(diào)度.

      提出問題,再用yield來解答,看到以上答案,我覺得呢,這PHP協(xié)程不過如此(Go協(xié)程相比 )。

      有句話——一個好問題比答案更重要,目前廣大網(wǎng)友還沒有給yield提出更好,更困難的問題。

      yield這個進進出出的語法,很多舉例都是再讓yield做迭代器啊,或者利用低內(nèi)存讀取超大文本的Excel,csv什么的,再高級就是用它實現(xiàn)一個簡單的任務調(diào)度器,并且這個調(diào)度器,一看代碼都差不多。

      我來出道題

      正如一個好的問題,比答案更有價值

      1. 用PHP實現(xiàn)一個 Socket Server,他能接收請求,并返回Server的時間。

      好,這是第一個問題,鋪墊。 官方答案

      1. 在原來的代碼上,我們加個需求,該Socket Server 處理請求時,依賴其他 Socket Server,還需要有 Client 功能。也就是他能接收請求,向其它Server發(fā)起請求。

      這是第二個問題,也是鋪墊。

      1. 原來的Socket Server同一時間只能服務一個客戶,希望能實現(xiàn)一個 非阻塞I/O Socket Server, 這個 Server 內(nèi)有 Socket Client 功能,支持并發(fā)處理收到的請求,和主動發(fā)起的請求。要求不用多線程,多進程。

      這個問題,還是鋪墊,這幾個問題很干,大家可以想一想,2,3題的答案,都放在一個腳本里了:nio_server.php

      以上這段代碼,我列舉了一個具體的業(yè)務,就是用戶請求購物車加購動作, 而購物車服務呢,又需要和 產(chǎn)品服務,庫存服務,優(yōu)惠服務 交互,來驗證加購動作可行性。有同步,異步方式請求,并做對比。

      后續(xù)還有很多代碼,我都放gitee鏈接了。使用方法,見readme.md

      1. 最后一個問題:在PHP中,用同步寫代碼,程序呢異步執(zhí)行?需要怎么調(diào)整代碼。

      提示:這個和 PHPyield 語法有關。

      再提示:yield 語法特征是什么,進進出出!

      看著我們的代碼,同步, 異步,進進出出 你想到了什么?

      看到代碼,同步處理模式下,這三個函數(shù)checkInventory checkProduct checkPromo 時,發(fā)起請求,并依次等待返回的結(jié)果,這三個函數(shù)執(zhí)行后,再響應客戶請求。

      異步處理模式下,這三個函數(shù)發(fā)起請求完畢后,代碼就跳出循環(huán)了,然后是在select()下的一個代碼分支中接收請求, 并收集結(jié)果。每次收到結(jié)果后判斷是否完成,完成則響應客戶端。

      那么能不能這樣:在異步處理的流程中,當 Server收到 自己發(fā)起的 client 有數(shù)據(jù)響應后,代碼跳到 nio_server.php 的 247行呢,這樣我們的收到請求校驗相關的代碼就能放到這里,編碼能就是同步,容易理解。不然,client 的響應處理放在 280 行以后,不通過抓包,真的很難理解,執(zhí)行了第 247 行代碼后,緊接著是從 280 行開始的。

      誒~這里是不是有 進進出出 那種感覺了~ 代碼從 247 行出去,開始監(jiān)聽發(fā)出 Client 響應,收到返回數(shù)據(jù),帶著數(shù)據(jù)再回到 247 行,繼續(xù)進行邏輯校驗,綜合結(jié)果后,再響應給客戶端。

      用yield來解決問題

      基于 yield 實現(xiàn)的,同步編碼,"異步"I/OSocket Server 就實現(xiàn)了。代碼。

      這里 “異步” 打了引號,大佬別扣這個字眼了。 該是非阻塞I/O

      不等大家的答案了,先上我的結(jié)果代碼吧,代碼呢都放在這個目錄下了。

      gitee https://gitee.com/xupaul/PHP-generator-yield-Demo/tree/master/yield-socket

      運行測試代碼

      clone 代碼到本地后,需要拉起4個 command 命令程序:

      拉起3個第三方服務

      ## 啟動一個處理耗時2s的庫存服務$ php ./other_server.php 8081 inventory 2## 啟動一個處理耗時4s的產(chǎn)品服務$ php ./other_server.php 8082 product 4## 監(jiān)聽8083端口,處理一個請求 耗時6s的 promo 服務$ php ./other_server.php 8083 promo 6

      啟動購物車服務

      ## 啟動一個非阻塞購物車服務$ php ./async_cart_server.php   ## 或者啟動一個一般購物車服務$ php ./cart_server.php

      發(fā)起用戶請求

      $ php ./user_client.php

      運行結(jié)果呢如下,通過執(zhí)行的時間日志,可得這三個請求是并發(fā)發(fā)起的,不是阻塞通訊。

      在看我們的代碼,三個函數(shù),發(fā)起socket請求,沒有設置callback,而是通過yield from 接收了三個socket的返回結(jié)果。

      也就是達到了,同步編碼,異步執(zhí)行的效果。

      運行結(jié)果

      非阻塞模式

      client 端日志:
      了解PHP yield的高級用法

      通過以上 起始時間結(jié)束時間 ,就看到這三個請求耗時總共就6s,也就按照耗時最長的promo服務的耗時來的。也就是說三個第三方請求都是并發(fā)進行的。

      cart server 端日志:
      了解PHP yield的高級用法

      而 cart 打印的日志,可以看到三個請求一并發(fā)起,并一起等待結(jié)果返回。達到非阻塞并發(fā)請求的效果。

      阻塞模式

      client 端日志:
      了解PHP yield的高級用法

      以上是阻塞方式請求,可以看到耗時 12s。也就是三個服務加起來的耗時。

      cart server 端日志:
      了解PHP yield的高級用法

      cart 服務,依次阻塞方式請求第三方服務,順序執(zhí)行完畢后,共耗時12s,當然如果第一個,獲第二個服務報錯的話,會提前結(jié)束這個檢查。會節(jié)約一點時間。

      工作原理

      這里就是用到了 yield 的工作特點——進進出出,在發(fā)起非阻塞socket請求后,不是阻塞方式等待socket響應,而是使用yield跳出當前執(zhí)行生成器,等待有socket響應后,在調(diào)用生成器的send方法回到發(fā)起socket請求的函數(shù)內(nèi),在 yield from Async::all() 接收數(shù)據(jù)響應數(shù)據(jù)搜集完畢后,返回。

      和Golang比一比

      考慮到網(wǎng)速原因,我這就放上一個國內(nèi)教程鏈接:Go 并發(fā) 教程

      php的協(xié)程是真協(xié)程,而Go是披著協(xié)程外衣的輕量化線程(“協(xié)程”里,都玩上“鎖”了,這就是線程)。

      我個人偏愛,協(xié)程的,覺得線程的調(diào)度有一定隨機性,因此需要鎖機制來保證程序的正確,帶來了額外開銷。協(xié)程的調(diào)度(換入換出)交給了用戶,保證了一段代碼執(zhí)行連續(xù)性(當然進程級上,還是會有換入換出的,除非是跨進程的資源訪問,或者跨機器的資源訪問,這時,就要用到分布式鎖了,這里不展開討論),同步編碼,異步執(zhí)行,只需要考慮那個哪個方法會有IO交互會協(xié)程跳出即可。

      和NodeJS比劃一下

      Javascript 和 PHP 兩個腳本語言有很多相似的地方,弱類型,動態(tài)對象,單線程,在Web領域生態(tài)豐富。不同的是,Javascript在瀏覽器端一開始就是異步的(如果js發(fā)起網(wǎng)絡請求只能同步進行,那么你的網(wǎng)頁渲染線程會卡住),例如Ajax,setTimeout,setInterval,這些都是異步+回調(diào)的方式工作。

      基于V8引擎而誕生的NodeJS,天生就是異步的,在提供高性能網(wǎng)絡服務有很大的優(yōu)勢,不過它的IO編碼范式么。。。剛開始是 回調(diào)——毀掉地獄,后來有了Promise——屏幕豎起來看,以及Generator——遇事不絕yield一下吧,到現(xiàn)在的Async/Await——語法糖?真香!

      可以說JS的委員非常勤快,在異步編程范式的標準制定也做的很好(以前我嘗試寫NodeJS時,幾個回調(diào)就直接把我勸退了),2009年誕生的NodeJS有點后來居上的意思。目前PHP只是趕上了協(xié)程,期待PHP的Async/Await語法糖的實現(xiàn)吧。

      PHP yield 使用注意事項

      一旦使用上 yield 后,就必須注意調(diào)用函數(shù)是,會得到函數(shù)結(jié)果,還是 生成器對象。PHP 不會自動幫你區(qū)別,需要你手動代碼判斷結(jié)果類型—— if ($re instanceof Generator) {}, 如果你得到的是 生成器,但不希望去手動調(diào)用 current() 去執(zhí)行它,那么在生成器前 使用 yield from 交給上游(框架)來解決。

      爆改 Workerman

      博客寫到這,就開始手癢癢了,看到Workerman框架,我在基礎上二開,使其能——同步編碼,異步執(zhí)行。

      代碼已放到:PaulXu-cn/CoWorkerman.git

      目前還是dev階段,大家喜歡可以先 體驗一波。

      $ composer require paulxu-cn/co-workerman

      一個簡單的單線程 TCP Server

      <?php// file: ./examples/example2/coWorkermanServer.php , 詳細代碼見github$worker = new CoWorker('tcp://0.0.0.0:8080');// 設置fork一個子進程$worker->count = 1;$worker->onConnect = function (CoTcpConnection  $connection) {     try {         $conName = "{$connection->getRemoteIp()}:{$connection->getRemotePort()}";         echo PHP_EOL . "New Connection, {$conName} n";          $re = yield from $connection->readAsync(1024);         CoWorker::safeEcho('get request msg :' . $re . PHP_EOL );          yield from CoTimer::sleepAsync(1000 * 2);          $connection->send(json_encode(array('productId' => 12, 're' =>true)));          CoWorker::safeEcho('Response to :' . $conName . PHP_EOL . PHP_EOL);     } catch (ConnectionCloseException $e) {         CoWorker::safeEcho('Connection closed, ' . $e->getMessage() . PHP_EOL);     }};CoWorker::runAll();

      這里設置fork 一個worker線程,處理邏輯中帶有一個sleep() 2s的操作,依然不影響他同時響應多個請求。

      啟動測試程序

      ## 啟動CoWorker服務$ php ./examples/example2/coWorkermanServer.php start## 啟動請求線程$ php ./examples/example2/userClientFork.php

      運行結(jié)果

      了解PHP yield的高級用法

      綠色箭頭——新的請求,紅色箭頭——響應請求

      從結(jié)果上看到,這一個worker線程,在接收新的請求同時,還在回復之前的請求,各個連接交錯運行。而我們的代碼呢,看樣子就是同步的,沒有回調(diào)。

      CoWorker購物車服務

      好的,這里我們做幾個簡單的微服務模擬實際應用,這里模擬 用戶請求端,購物車服務,庫存服務產(chǎn)品服務。 模擬用戶請求加購動作,購物車去分別請求 庫存,產(chǎn)品 校驗用戶是否可以加購,并響應客戶請求是否成功。

      代碼我就不貼了,太長了,麻煩移步 CoWorkerman/example/example5/coCartServer.php

      運行命令

      ## 啟動庫存服務$ php ./examples/example5/otherServerFork.php 8081 inventory 1## 啟動產(chǎn)品服務$ php ./examples/example5/otherServerFork.php  8082 product 2
      ## 啟動CoWorker 購物車服務$ php ./examples/example5/coCartServer.php start
      ## 用戶請求端$ php ./examples/example5/userClientFork.php

      運行結(jié)果

      了解PHP yield的高級用法

      黃色箭頭——新的用戶請求,藍色箭頭——購物車發(fā)起庫存,產(chǎn)品檢查請求,紅色箭頭——響應用戶請求

      從圖中看到也是用1個線程服務多個連接,交錯運行。

      好的,那么PHP CoWorkerman 也能像 NodeJS 那樣用 Async/Await 那樣同步編碼,異步運行了。

      快來試試這個 CoWorkerman 吧:

      $ composer require paulxu-cn/co-workerman

      工作原理

      先上圖:了解PHP yield的高級用法

      圖的上部是Workerman 的工作泳道圖,圖下部是CoWorkerman的工作泳道圖。

      workerman內(nèi)的worker進程遇到阻塞函數(shù)的處理方式時,會等待IO返回,如果這個時候,又有了新的請求,那么閑的worker會競爭到這個新的連接。

      我在上圖worker5中,描述了一個AsyncTCPConnection使用情況,woker內(nèi)發(fā)起了一個非阻塞請求,并注冊了回調(diào)函數(shù),然后程序繼續(xù)運行到結(jié)束。當異步請求響應時,就需要通過其他方式去響應(如自己再發(fā)起一個請求告知請求方)。

      在下圖中CoWorkerman,也是多個Worker競爭新的請求,當worker1收到一個新的請求,會產(chǎn)生一個生成器,生成器內(nèi)發(fā)起異步請求,并注冊響應回調(diào),請求響應后,回到該生成器跳出(yield)的地方,繼續(xù)執(zhí)行代碼。

      發(fā)起異步請求,并注冊回調(diào)函數(shù),這些默認工作CoWorkerman框架內(nèi)已做了,回調(diào)函數(shù)內(nèi)工作是:收到數(shù)據(jù),并發(fā)給 發(fā)起該請求的生成器。

      這例子中,通過調(diào)用 Promise:all() 發(fā)起多個請求,并監(jiān)聽結(jié)果返回,待所有的響應返回再繼續(xù)運行生成器

      在程序yield跳出后,該worker就處于事件循環(huán)狀態(tài)($event->loop()),也就是多路監(jiān)聽:請求端口,第三方客戶端請求響應端口。這個時候如果:

      1. 有新的請求來,他和其他 worker 競爭新的請求,如果競爭到了,則該worker內(nèi)又產(chǎn)生一個新的 生成器。
      2. 客戶端有響應,則調(diào)用回調(diào)函數(shù)
      3. 客戶端都響應了,繼續(xù)運行 生成器程序。

      從1中,我們可假設,如果就一個 Worker,那么該 Worker 可以在上一個請求未完成情況下,繼續(xù)接受處理下一個請求。也就是 CoWorkerman 可以在單 Worker 下運行,并發(fā)處理多個請求。

      當然,這里也有個前提,單 Worker 模式內(nèi)不能運行阻塞函數(shù),一旦阻塞,后續(xù)請求就會堵在網(wǎng)卡。所以,除非對自己的代碼非常了解,如果用到第三方庫,那么我還是建議你在多 Worker 模式下運行 CoWorkerman,阻塞時,還有其他Worker兜住新請求。

      CoWorkerman 的意義

      1. 用同步的代碼,發(fā)起異步請求,多個請求可并發(fā),從IO串行等待,改為并行等待,減少無畏的等待時間。提高業(yè)務程序的效率同時,不降低代碼可讀性。
      2. 在一個線程內(nèi)通過事件循環(huán),盡可能處理多個請求,緩解了一個請求一個線程帶來的頻繁線程切換,從核心上提高運行效率。

      CoWorkerman 生態(tài)位

      適合處理純Socket請求的應用,如Workerman Gateway,或者是 大前端 整合多個服務RPC結(jié)果, 綜合后返給前三頁這樣的場景.

      日志記錄是每個程序最基本需求,由于寫文件函數(shù)是阻塞的,建議用消息隊列,或者redis隊列,更或者跳過Logstash直接丟Elasticsearch.

      CoWorkerman有他的局限性,也有他自己位置。

      總結(jié)

      好~PHP 協(xié)程編碼到 網(wǎng)絡異步編碼就到此結(jié)束了,如果看到本文章有很多疑惑,歡迎留言提問,如果是 yield 語法不太記得,可以先讀一讀這個系列前幾篇文章復習一下。

      如果行,請三連。CoWorkerman 謝謝!

      了解PHP yield的高級用法

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號