天天看點

PHP異步的玩法

php是世界上最好的語言,但是總被“同行們”吐槽不支援異步。其實我們要實作異步也非常簡單,之前看到鳥哥的一篇寫php異步執行的博文 php實作異步調用方法研究,這篇文章還是08年的,到今天php發展快10年了,對于異步調用也有了更多新的玩法。

一是通過渲染前端頁面,使用js執行ajax,這種方式現在還适用。隻是受限于業務場景,因為隻能在浏覽器中調用,遇到接口請求就不行了。

二是通過popen()方法打開一個指向程序的管道,每個請求會多起一個程序。忽略程序來看最主要的原因是資料的傳輸特别不友善,使用場景有限。

三是使用curl擴充,通過設定timeout逾時參數,能實作離弦之箭的效果。不過這種方法會主動斷開連接配接。被調用的服務如果有做連接配接檢測,也會中斷服務端腳本的執行。比如我們請求 微信的某個費時接口(20s),我們調用1s就斷開連接配接,微信端是否會維持請求執行20s是不可控的。是以這種方法不推薦大家使用。

四方法與curl類似,通過fsockopen建立socket連接配接通路遠端服務,不循環擷取請求結果。一樣會有三中連接配接被斷開的問題。

curl擴充已支援毫秒配置,将 curlopt_timeout 改為 curlopt_timeout_ms 即可生效(curl 版本 >= libcurl/7.21.0,老伺服器要檢查版本),但還是我前面說的需要服務端配合,不然接口的調用結果不可控。

curl擴充已支援并發,我們能一次通路n個接口,執行時間取最長接口的時間。比如我們能一次通路 京東支付(1s),微信支付(1.2s),支付寶(0.8s)不同服務的三個接口,總耗時才1.2s。詳細用法 curl_multi_init

類似node.js的異步io架構swoole,能很好的實作異步調用;不過swoole理論上不能算php架構,他算是php功能的擴充。是以除非項目都用swoole寫,不然也是享受不到異步io的福利。

對yield的支援,能實作排程器的功能,寫單程序的服務時能大展拳腳,特别是實作協程,異步更不在話下。不過在多程序的web服務上沒有太大的使用場景,看未來會不會有新的玩法吧。

當然還有很多新的特性,這裡不再細說,總之php越是被黑越是能快速發展。

我給這個功能取了一個很生動的名字--離弦之箭。代表異步調用,我們的弓箭射出去後并不關心它的結果因為發送這個動作做了就行。比如打個10m的log,通知10個人(發10條短信)。

代碼說明:首先arrow類是個單例類,減少多次調用的開銷。run()方法傳遞的是一個匿名函數,這樣我們能非常友善的傳遞參數,并且保留上下文。

這個類最難的地方在于多程序的處理。因為我們要盡可能快的将資料傳回給使用者,是以主程序越快結束越好。但是我們又需要子程序來執行我們耗時的操作,執行完退出才行。如果不等子程序執行完就将父程序退出會出現什麼結果呢?結果就是子程序會常駐記憶體變成僵死程序。那我們有什麼辦法讓子程序執行完之後就自動結束呢?答案是很難……那麼兒子程序這麼不聽話,孫子程序會不會聽話一點呢??答案是孫子程序執行結束後會被系統程序回收并銷毀(還是孫子聽話)。是以我在代碼中使用了如下方法:目前請求程序fork出子程序,子程序fork出孫子程序,主程序和子程序都先行退出,最後由孫子程序來執行耗時操作,最後完美的解決了僵死程序問題。