中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
PHP并發(fā)IO編程之路[轉(zhuǎn)]
原文出處: 韓天峰(@韓天峰-Rango)   

并發(fā)IO問(wèn)題一直是后端編程中的技術(shù)挑戰(zhàn),從最早的同步阻塞Fork進(jìn)程,到多進(jìn)程/多線程,到現(xiàn)在的異步IO、協(xié)程。PHP程序員因?yàn)橛袕?qiáng)大的LAMP框架,對(duì)底層方面的知識(shí)知之甚少,本文目的就是詳細(xì)介紹PHP進(jìn)行并發(fā)IO編程的各種嘗試,最后再介紹Swoole的使用,深入淺出全面理解并發(fā)IO問(wèn)題。

多進(jìn)程/多線程同步阻塞

最早的服務(wù)器端程序都是通過(guò)多進(jìn)程、多線程來(lái)解決并發(fā)IO的問(wèn)題。進(jìn)程模型出現(xiàn)的最早,從Unix系統(tǒng)誕生就開(kāi)始有了進(jìn)程的概念。最早的服務(wù)器端程序一般都是Accept一個(gè)客戶端連接就創(chuàng)建一個(gè)進(jìn)程,然后子進(jìn)程進(jìn)入循環(huán)同步阻塞地與客戶端連接進(jìn)行交互,收發(fā)處理數(shù)據(jù)。

多線程模式出現(xiàn)要晚一些,線程與進(jìn)程相比更輕量,而且線程之間是共享內(nèi)存堆棧的,所以不同的線程之間交互非常容易實(shí)現(xiàn)。比如聊天室這樣的程序,客戶端連接之間可以交互,比聊天室中的玩家可以任意的其他人發(fā)消息。用多線程模式實(shí)現(xiàn)非常簡(jiǎn)單,線程中可以直接讀寫(xiě)某一個(gè)客戶端連接。而多進(jìn)程模式就要用到管道、消息隊(duì)列、共享內(nèi)存實(shí)現(xiàn)數(shù)據(jù)交互,統(tǒng)稱(chēng)進(jìn)程間通信(IPC)復(fù)雜的技術(shù)才能實(shí)現(xiàn)。

代碼實(shí)例:

多進(jìn)程/線程模型的流程是:

  1. 創(chuàng)建一個(gè) socket,綁定服務(wù)器端口(bind),監(jiān)聽(tīng)端口(listen),在PHP中用stream_socket_server一個(gè)函數(shù)就能完成上面3個(gè)步驟,當(dāng)然也可以使用php sockets擴(kuò)展分別實(shí)現(xiàn)。
  2. 進(jìn)入while循環(huán),阻塞在accept操作上,等待客戶端連接進(jìn)入。此時(shí)程序會(huì)進(jìn)入隨眠狀態(tài),直到有新的客戶端發(fā)起connect到服務(wù)器,操作系統(tǒng)會(huì)喚醒此進(jìn)程。accept函數(shù)返回客戶端連接的socket
  3. 主進(jìn)程在多進(jìn)程模型下通過(guò)fork(php: pcntl_fork)創(chuàng)建子進(jìn)程,多線程模型下使用pthread_create(php: new Thread)創(chuàng)建子線程。下文如無(wú)特殊聲明將使用進(jìn)程同時(shí)表示進(jìn)程/線程。
  4. 子進(jìn)程創(chuàng)建成功后進(jìn)入while循環(huán),阻塞在recv(php: fread)調(diào)用上,等待客戶端向服務(wù)器發(fā)送數(shù)據(jù)。收到數(shù)據(jù)后服務(wù)器程序進(jìn)行處理然后使用send(php: fwrite)向客戶端發(fā)送響應(yīng)。長(zhǎng)連接的服務(wù)會(huì)持續(xù)與客戶端交互,而短連接服務(wù)一般收到響應(yīng)就會(huì)close。
  5. 當(dāng)客戶端連接關(guān)閉時(shí),子進(jìn)程退出并銷(xiāo)毀所有資源。主進(jìn)程會(huì)回收掉此子進(jìn)程。

這種模式最大的問(wèn)題是,進(jìn)程/線程創(chuàng)建和銷(xiāo)毀的開(kāi)銷(xiāo)很大。所以上面的模式?jīng)]辦法應(yīng)用于非常繁忙的服務(wù)器程序。對(duì)應(yīng)的改進(jìn)版解決了此問(wèn)題,這就是經(jīng)典的Leader-Follower模型。

代碼實(shí)例:

它的特點(diǎn)是程序啟動(dòng)后就會(huì)創(chuàng)建N個(gè)進(jìn)程。每個(gè)子進(jìn)程進(jìn)入Accept,等待新的連接進(jìn)入。當(dāng)客戶端連接到服務(wù)器時(shí),其中一個(gè)子進(jìn)程會(huì)被喚醒,開(kāi)始處理客戶端請(qǐng)求,并且不再接受新的TCP連接。當(dāng)此連接關(guān)閉時(shí),子進(jìn)程會(huì)釋放,重新進(jìn)入Accept,參與處理新的連接。

這個(gè)模型的優(yōu)勢(shì)是完全可以復(fù)用進(jìn)程,沒(méi)有額外消耗,性能非常好。很多常見(jiàn)的服務(wù)器程序都是基于此模型的,比如Apache、PHP-FPM。

多進(jìn)程模型也有一些缺點(diǎn)。

  1. 這種模型嚴(yán)重依賴(lài)進(jìn)程的數(shù)量解決并發(fā)問(wèn)題,一個(gè)客戶端連接就需要占用一個(gè)進(jìn)程,工作進(jìn)程的數(shù)量有多少,并發(fā)處理能力就有多少。操作系統(tǒng)可以創(chuàng)建的進(jìn)程數(shù)量是有限的。
  2. 啟動(dòng)大量進(jìn)程會(huì)帶來(lái)額外的進(jìn)程調(diào)度消耗。數(shù)百個(gè)進(jìn)程時(shí)可能進(jìn)程上下文切換調(diào)度消耗占CPU不到1%可以忽略不接,如果啟動(dòng)數(shù)千甚至數(shù)萬(wàn)個(gè)進(jìn)程,消耗就會(huì)直線上升。調(diào)度消耗可能占到CPU的百分之幾十甚至100%。

另外有一些場(chǎng)景多進(jìn)程模型無(wú)法解決,比如即時(shí)聊天程序(IM),一臺(tái)服務(wù)器要同時(shí)維持上萬(wàn)甚至幾十萬(wàn)上百萬(wàn)的連接(經(jīng)典的C10K問(wèn)題),多進(jìn)程模型就力不從心了。

還有一種場(chǎng)景也是多進(jìn)程模型的軟肋。通常Web服務(wù)器啟動(dòng)100個(gè)進(jìn)程,如果一個(gè)請(qǐng)求消耗100ms,100個(gè)進(jìn)程可以提供1000qps,這樣的處理能力還是不錯(cuò)的。但是如果請(qǐng)求內(nèi)要調(diào)用外網(wǎng)Http接口,像QQ、微博登錄,耗時(shí)會(huì)很長(zhǎng),一個(gè)請(qǐng)求需要10s。那一個(gè)進(jìn)程1秒只能處理0.1個(gè)請(qǐng)求,100個(gè)進(jìn)程只能達(dá)到10qps,這樣的處理能力就太差了。

有沒(méi)有一種技術(shù)可以在一個(gè)進(jìn)程內(nèi)處理所有并發(fā)IO呢?答案是有,這就是IO復(fù)用技術(shù)。

IO復(fù)用/事件循環(huán)/異步非阻塞

其實(shí)IO復(fù)用的歷史和多進(jìn)程一樣長(zhǎng),Linux很早就提供了select系統(tǒng)調(diào)用,可以在一個(gè)進(jìn)程內(nèi)維持1024個(gè)連接。后來(lái)又加入了poll系統(tǒng)調(diào)用,poll做了一些改進(jìn),解決了1024限制的問(wèn)題,可以維持任意數(shù)量的連接。但select/poll還有一個(gè)問(wèn)題就是,它需要循環(huán)檢測(cè)連接是否有事件。這樣問(wèn)題就來(lái)了,如果服務(wù)器有100萬(wàn)個(gè)連接,在某一時(shí)間只有一個(gè)連接向服務(wù)器發(fā)送了數(shù)據(jù),select/poll需要做循環(huán)100萬(wàn)次,其中只有1次是命中的,剩下的99萬(wàn)9999次都是無(wú)效的,白白浪費(fèi)了CPU資源。

直到Linux 2.6內(nèi)核提供了新的epoll系統(tǒng)調(diào)用,可以維持無(wú)限數(shù)量的連接,而且無(wú)需輪詢,這才真正解決了C10K問(wèn)題?,F(xiàn)在各種高并發(fā)異步IO的服務(wù)器程序都是基于epoll實(shí)現(xiàn)的,比如Nginx、Node.js、Erlang、Golang。像Node.js這樣單進(jìn)程單線程的程序,都可以維持超過(guò)1百萬(wàn)TCP連接,全部歸功于epoll技術(shù)。

IO復(fù)用異步非阻塞程序使用經(jīng)典的Reactor模型,Reactor顧名思義就是反應(yīng)堆的意思,它本身不處理任何數(shù)據(jù)收發(fā)。只是可以監(jiān)視一個(gè)socket句柄的事件變化。

Reactor有4個(gè)核心的操作:

  1. add添加socket監(jiān)聽(tīng)到reactor,可以是listen socket也可以使客戶端socket,也可以是管道、eventfd、信號(hào)等
  2. set修改事件監(jiān)聽(tīng),可以設(shè)置監(jiān)聽(tīng)的類(lèi)型,如可讀、可寫(xiě)??勺x很好理解,對(duì)于listen socket就是有新客戶端連接到來(lái)了需要accept。對(duì)于客戶端連接就是收到數(shù)據(jù),需要recv??蓪?xiě)事件比較難理解一些。一個(gè)SOCKET是有緩存區(qū)的,如果要向客戶端連接發(fā)送2M的數(shù)據(jù),一次性是發(fā)不出去的,操作系統(tǒng)默認(rèn)TCP緩存區(qū)只有256K。一次性只能發(fā)256K,緩存區(qū)滿了之后send就會(huì)返回EAGAIN錯(cuò)誤。這時(shí)候就要監(jiān)聽(tīng)可寫(xiě)事件,在純異步的編程中,必須去監(jiān)聽(tīng)可寫(xiě)才能保證send操作是完全非阻塞的。
  3. del從reactor中移除,不再監(jiān)聽(tīng)事件
  4. callback就是事件發(fā)生后對(duì)應(yīng)的處理邏輯,一般在add/set時(shí)制定。C語(yǔ)言用函數(shù)指針實(shí)現(xiàn),JS可以用匿名函數(shù),PHP可以用匿名函數(shù)、對(duì)象方法數(shù)組、字符串函數(shù)名。

Reactor只是一個(gè)事件發(fā)生器,實(shí)際對(duì)socket句柄的操作,如connect/accept、send/recv、close是在callback中完成的。具體編碼可參考下面的偽代碼:

Reactor模型還可以與多進(jìn)程、多線程結(jié)合起來(lái)用,既實(shí)現(xiàn)異步非阻塞IO,又利用到多核。目前流行的異步服務(wù)器程序都是這樣的方式:如

  • Nginx:多進(jìn)程Reactor
  • Nginx+Lua:多進(jìn)程Reactor+協(xié)程
  • Golang:?jiǎn)尉€程Reactor+多線程協(xié)程
  • Swoole:多線程Reactor+多進(jìn)程Worker

協(xié)程是什么

協(xié)程從底層技術(shù)角度看實(shí)際上還是異步IO Reactor模型,應(yīng)用層自行實(shí)現(xiàn)了任務(wù)調(diào)度,借助Reactor切換各個(gè)當(dāng)前執(zhí)行的用戶態(tài)線程,但用戶代碼中完全感知不到Reactor的存在。

PHP并發(fā)IO編程實(shí)踐

PHP相關(guān)擴(kuò)展

  • Stream:PHP內(nèi)核提供的socket封裝
  • Sockets:對(duì)底層Socket API的封裝
  • Libevent:對(duì)libevent庫(kù)的封裝
  • Event:基于Libevent更高級(jí)的封裝,提供了面向?qū)ο蠼涌?、定時(shí)器、信號(hào)處理的支持
  • Pcntl/Posix:多進(jìn)程、信號(hào)、進(jìn)程管理的支持
  • Pthread:多線程、線程管理、鎖的支持
  • PHP還有共享內(nèi)存、信號(hào)量、消息隊(duì)列的相關(guān)擴(kuò)展
  • PECL:PHP的擴(kuò)展庫(kù),包括系統(tǒng)底層、數(shù)據(jù)分析、算法、驅(qū)動(dòng)、科學(xué)計(jì)算、圖形等都有。如果PHP標(biāo)準(zhǔn)庫(kù)中沒(méi)有找到,可以在PECL尋找想要的功能。

PHP語(yǔ)言的優(yōu)劣勢(shì)

PHP的優(yōu)點(diǎn):

  1. 第一個(gè)是簡(jiǎn)單,PHP比其他任何的語(yǔ)言都要簡(jiǎn)單,入門(mén)的話PHP真的是可以一周就入門(mén)。C++有一本書(shū)叫做《21天深入學(xué)習(xí)C++》,其實(shí)21天根本不可能學(xué)會(huì),甚至可以說(shuō)C++沒(méi)有3-5年不可能深入掌握。但是PHP絕對(duì)可以7天入門(mén)。所以PHP程序員的數(shù)量非常多,招聘比其他語(yǔ)言更容易。
  2. PHP的功能非常強(qiáng)大,因?yàn)镻HP官方的標(biāo)準(zhǔn)庫(kù)和擴(kuò)展庫(kù)里提供了做服務(wù)器編程能用到的99%的東西。PHP的PECL擴(kuò)展庫(kù)里你想要的任何的功能。

另外PHP有超過(guò)20年的歷史,生態(tài)圈是非常大的,在Github可以找到很多代碼。

PHP的缺點(diǎn):

  1. 性能比較差,因?yàn)楫吘故莿?dòng)態(tài)腳本,不適合做密集運(yùn)算,如果同樣用PHP寫(xiě)再用c++寫(xiě),PHP版本要比它差一百倍。
  2. 函數(shù)命名規(guī)范差,這一點(diǎn)大家都是了解的,PHP更講究實(shí)用性,沒(méi)有一些規(guī)范。一些函數(shù)的命名是很混亂的,所以每次你必須去翻PHP的手冊(cè)。
  3. 提供的數(shù)據(jù)結(jié)構(gòu)和函數(shù)的接口粒度比較粗。PHP只有一個(gè)Array數(shù)據(jù)結(jié)構(gòu),底層基于HashTable。PHP的Array集合了Map,Set,Vector,Queue,Stack,Heap等數(shù)據(jù)結(jié)構(gòu)的功能。另外PHP有一個(gè)SPL提供了其他數(shù)據(jù)結(jié)構(gòu)的類(lèi)封裝。

所以PHP:

  1. PHP更適合偏實(shí)際應(yīng)用層面的程序,業(yè)務(wù)開(kāi)發(fā)、快速實(shí)現(xiàn)的利器
  2. PHP不適合開(kāi)發(fā)底層軟件
  3. 使用C/C++、JAVA、Golang等靜態(tài)編譯語(yǔ)言作為PHP的補(bǔ)充,動(dòng)靜結(jié)合
  4. 借助IDE工具實(shí)現(xiàn)自動(dòng)補(bǔ)全、語(yǔ)法提示

PHP的Swoole擴(kuò)展

基于上面的擴(kuò)展使用純PHP就可以完全實(shí)現(xiàn)異步網(wǎng)絡(luò)服務(wù)器和客戶端程序。但是想實(shí)現(xiàn)一個(gè)類(lèi)似于多IO線程,還是有很多繁瑣的編程工作要做,包括如何來(lái)管理連接,如何來(lái)保證數(shù)據(jù)的收發(fā)原則性,網(wǎng)絡(luò)協(xié)議的處理。另外PHP代碼在協(xié)議處理部分性能是比較差的,所以我啟動(dòng)了一個(gè)新的開(kāi)源項(xiàng)目Swoole,使用C語(yǔ)言和PHP結(jié)合來(lái)完成了這項(xiàng)工作。靈活多變的業(yè)務(wù)模塊使用PHP開(kāi)發(fā)效率高,基礎(chǔ)的底層和協(xié)議處理部分用C語(yǔ)言實(shí)現(xiàn),保證了高性能。它以擴(kuò)展的方式加載到了PHP中,提供了一個(gè)完整的網(wǎng)絡(luò)通信的框架,然后PHP的代碼去寫(xiě)一些業(yè)務(wù)。它的模型是基于多線程Reactor+多進(jìn)程Worker,既支持全異步,也支持半異步半同步。

Swoole的一些特點(diǎn):

  • Accept線程,解決Accept性能瓶頸和驚群?jiǎn)栴}
  • 多IO線程,可以更好地利用多核
  • 提供了全異步和半同步半異步2種模式
  • 處理高并發(fā)IO的部分用異步模式
  • 復(fù)雜的業(yè)務(wù)邏輯部分用同步模式
  • 底層支持了遍歷所有連接、互發(fā)數(shù)據(jù)、自動(dòng)合并拆分?jǐn)?shù)據(jù)包、數(shù)據(jù)發(fā)送原子性。

Swoole的進(jìn)程/線程模型:

Swoole程序的執(zhí)行流程:

使用PHP+Swoole擴(kuò)展實(shí)現(xiàn)異步通信編程

實(shí)例代碼在https://github.com/swoole/swoole-src 主頁(yè)查看。

TCP服務(wù)器與客戶端

異步TCP服務(wù)器:

在這里new swoole_server對(duì)象,然后參數(shù)傳入監(jiān)聽(tīng)的HOST和PORT,然后設(shè)置了3個(gè)回調(diào)函數(shù),分別是onConnect有新的連接進(jìn)入、onReceive收到了某一個(gè)客戶端的數(shù)據(jù)、onClose某個(gè)客戶端關(guān)閉了連接。最后調(diào)用start啟動(dòng)服務(wù)器程序。swoole底層會(huì)根據(jù)當(dāng)前機(jī)器有多少CPU核數(shù),啟動(dòng)對(duì)應(yīng)數(shù)量的Reactor線程和Worker進(jìn)程。

異步客戶端:

客戶端的使用方法和服務(wù)器類(lèi)似只是回調(diào)事件有4個(gè),onConnect成功連接到服務(wù)器,這時(shí)可以去發(fā)送數(shù)據(jù)到服務(wù)器。onError連接服務(wù)器失敗。onReceive服務(wù)器向客戶端連接發(fā)送了數(shù)據(jù)。onClose連接關(guān)閉。

設(shè)置完事件回調(diào)后,發(fā)起connect到服務(wù)器,參數(shù)是服務(wù)器的IP,PORT和超時(shí)時(shí)間。

同步客戶端:

同步客戶端不需要設(shè)置任何事件回調(diào),它沒(méi)有Reactor監(jiān)聽(tīng),是阻塞串行的。等待IO完成才會(huì)進(jìn)入下一步。

異步任務(wù):

異步任務(wù)功能用于在一個(gè)純異步的Server程序中去執(zhí)行一個(gè)耗時(shí)的或者阻塞的函數(shù)。底層實(shí)現(xiàn)使用進(jìn)程池,任務(wù)完成后會(huì)觸發(fā)onFinish,程序中可以得到任務(wù)處理的結(jié)果。比如一個(gè)IM需要廣播,如果直接在異步代碼中廣播可能會(huì)影響其他事件的處理。另外文件讀寫(xiě)也可以使用異步任務(wù)實(shí)現(xiàn),因?yàn)槲募浔鷽](méi)辦法像socket一樣使用Reactor監(jiān)聽(tīng)。因?yàn)槲募浔偸强勺x的,直接讀取文件可能會(huì)使服務(wù)器程序阻塞,使用異步任務(wù)是非常好的選擇。

異步毫秒定時(shí)器

這2個(gè)接口實(shí)現(xiàn)了類(lèi)似JS的setInterval、setTimeout函數(shù)功能,可以設(shè)置在n毫秒間隔實(shí)現(xiàn)一個(gè)函數(shù)或 n毫秒后執(zhí)行一個(gè)函數(shù)。

異步MySQL客戶端

swoole還提供一個(gè)內(nèi)置連接池的MySQL異步客戶端,可以設(shè)定最大使用MySQL連接數(shù)。并發(fā)SQL請(qǐng)求可以復(fù)用這些連接,而不是重復(fù)創(chuàng)建,這樣可以保護(hù)MySQL避免連接資源被耗盡。

異步Redis客戶端

異步的Web程序

程序的邏輯是從Redis中讀取一個(gè)數(shù)據(jù),然后顯示HTML頁(yè)面。使用ab壓測(cè)性能如下:

同樣的邏輯在php-fpm下的性能測(cè)試結(jié)果如下:

WebSocket程序

swoole內(nèi)置了websocket服務(wù)器,可以基于此實(shí)現(xiàn)Web頁(yè)面主動(dòng)推送的功能,比如WebIM。有一個(gè)開(kāi)源項(xiàng)目可以作為參考。https://github.com/matyhtf/php-webim

PHP+Swoole協(xié)程

異步編程一般使用回調(diào)方式,如果遇到非常復(fù)雜的邏輯,可能會(huì)層層嵌套回調(diào)函數(shù)。協(xié)程就可以解決此問(wèn)題,可以順序編寫(xiě)代碼,但運(yùn)行時(shí)是異步非阻塞的。騰訊的工程師基于Swoole擴(kuò)展和PHP5.5的Yield/Generator語(yǔ)法實(shí)現(xiàn)類(lèi)似于Golang的協(xié)程,項(xiàng)目名稱(chēng)為T(mén)SF(Tencent Server Framework),開(kāi)源項(xiàng)目地址:https://github.com/tencent-php/tsf。目前在騰訊公司的企業(yè)QQ、QQ公眾號(hào)項(xiàng)目以及車(chē)輪忽略的查違章項(xiàng)目有大規(guī)模應(yīng)用 。

TSF使用也非常簡(jiǎn)單,下面調(diào)用了3個(gè)IO操作,完全是串行的寫(xiě)法。但實(shí)際上是異步非阻塞執(zhí)行的。TSF底層調(diào)度器接管了程序的執(zhí)行,在對(duì)應(yīng)的IO完成后才會(huì)向下繼續(xù)執(zhí)行。

在樹(shù)莓派上使用PHP+Swoole

PHP和Swoole都可以在ARM平臺(tái)上編譯運(yùn)行,所以在樹(shù)莓派系統(tǒng)上也可以使用PHP+Swoole來(lái)開(kāi)發(fā)網(wǎng)絡(luò)通信的程序。

加入伯樂(lè)在線專(zhuān)欄作者。擴(kuò)大知名度,還能得贊賞!詳見(jiàn)《招募專(zhuān)欄作者
2 16 收藏 4 評(píng)論
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
【Swoole系列3.2】Swoole 異步進(jìn)程服務(wù)系統(tǒng)
PHP開(kāi)發(fā)者的福音,解讀PHP異步通信框架Swoole的機(jī)制
JAVA 中BIO,NIO,AIO的理解
BIO與NIO、AIO的區(qū)別(這個(gè)容易理解)
Swoole的進(jìn)程模型
要學(xué)swoole看這個(gè) 比看文檔強(qiáng)多了 (文末有福利)
更多類(lèi)似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服