首先,在網上找的資訊說PHP代碼執行的順序是這樣的,第一步是詞法分析,第二步是文法分析,第三步是轉化為opcode,第四部也就是順序執行這些opcode了。
聰明如各位看到這裡,再傳回上面看一下這張圖檔,肯定就會有不一樣的感覺了。咱們借用一句話來闡述這幾個步驟:當PHP拿到一段代碼後,經過詞法解析、文法解析等階段後,源程式會被翻譯成一個個指令(opcodes),然後ZEND虛拟機順次執行這些指令完成操作。
我們都知道,PHP本身是用C實作的,是以最終調用的也是C的函數,實際上,我們可以把PHP看做一個C開發的軟體。既然如此,那麼PHP的代碼執行的核心也就是翻譯出來的一條一條指令,在這裡就是opcode。
那麼,我們就可以把opcode看成是PHP代碼執行的最基本機關。是以PHP代碼執行的本質也就可以了解為,我們的代碼最終被翻譯為一組opcode處理函數,完事之後再順序執行。
有了這些認知之後,我們就來看下這個opcode究竟是什麼鬼。本質上一個opcode由兩個參數(op1,op2)、傳回值和處理函數組成。它的官方解釋就是PHP腳本編譯後的中間語言,類似于java中的bytecode或者是.net中的MSL。
它的作用就是如下:
1、編譯原理的中間過程會産生一種中間代碼(語言),PHP由Zend引擎(C語言編寫)編譯後的中間代碼為Opcode然後再交由Zend引擎處理,如同C語言編譯後彙編代碼然後再交由彙編。
2、生成的Opcode作為一種中間語言,可以幫助實作PHP源程式代碼的不開源,如果你不想别人知道你的PHP代碼是怎麼寫的,那你可以直接使用APC截取生成Opcode緩存檔案,然後使用自己的PHP擴充加密程式對Opcode檔案進行加密和解密,在Zend引擎對Opcode進行解析前進行解密然後再執行。
1、php-fpm 是如何處理web請求的?有什麼問題?
我們用的 PHP 主要用于 web 開發,通過 nginx、apache 等服務端程式調用 php-fpm 處理服務端的業務邏輯,處理完後 php 撤消記憶體并傳回結果。一個 web 請求就要加載一次 php 的全部檔案,需要的系統資源開銷很大,這是目前 php-fpm 的缺點之一;并且因為 php-fpm 在一次請求結束就釋放記憶體,無法做連接配接池,也不合适 service 端的開發。我們用的 PHP 主要用于 web 開發,通過 nginx、apache 等服務端程式調用 php-fpm 處理服務端的業務邏輯,處理完後 php 撤消記憶體并傳回結果。一個 web 請求就要加載一次 php 的全部檔案,需要的系統資源開銷很大,這是目前 php-fpm 的缺點之一;并且因為 php-fpm 在一次請求結束就釋放記憶體,無法做連接配接池,也不合适 service 端的開發。
下面是 php-fpm 的運作流程,各位可以參考一下:
http://www.test.cc
|
Nginx
|
路由到 http://www.test.cc/index.php
|
加載nginx的fast-cgi子產品
|
fast-cgi監聽127.0.0.1:9000位址
|
www.test.com/index.php請求到達127.0.0.1:9000
|
php-fpm 監聽127.0.0.1:9000
|
php-fpm 接收到請求,啟用worker程序處理請求
|
php-fpm 處理完請求并撤消記憶體,傳回給nginx
|
nginx 将結果通過http傳回給浏覽器
2.swoole是如何解決php-fpm遇到的問題的?
swoole如何避免檔案的反複加載:
swoole是完全的長駐記憶體的,長駐記憶體一個最大的好處就是可以性能加速。在fpm模式下,我們處理一個請求,通常會有一些空消耗,比如架構共用檔案加載,配置檔案加載,那麼在swoole中,可以在onworkerstart的時候提前一次性把一些必要的檔案和配置加載好,不必每次receive重複加載一遍,這樣能提升不小的性能。
常駐記憶體
常駐記憶體。傳統 PHP架構或者單檔案,在處理每個請求之前,都要做一遍加載架構檔案、配置的操作,請求完成之後會釋放所有資源和記憶體,無須擔心記憶體洩漏。但是如果請求數量上升,并發很高的時候,快速建立資源,又馬上釋放,會導緻 PHP 程式運作效率急劇下降。而使用 Swoole 則沒有這個問題:PHP的代碼加載到記憶體後,擁有更長的生命周期,這樣建立的資料庫連接配接和其他大的對象,不被釋放。每次請求隻需要處理很少的代碼,而這些代碼隻在第一次運作時,被 PHP 解析器編譯,駐留記憶體。以後都是直接載入 OPCODE ,讓 Zend 引擎直接運作。另外,之前PHP不能實作的,如資料庫連接配接池,緩存連接配接池都可以在Swoole引擎下實作。系統的運作效率會大大提高。
swoole如何實作高并發
請求到達 Main Reactor
|
Main Reactor 根據 Reactor 的情況,将請求注冊給對應的 Reactor
(每個 Reactor 都有 epoll,用來監聽用戶端的變化)
|
用戶端有變化時,交給 worker 來處理
|
worker 處理完畢,通過程序間通信(比如管道、共享記憶體、消息隊列)發給對應的 reactor
|
reactor 将響應結果發給相應的連接配接
|
請求處理完成
因為reactor基于epoll,是以每個reactor可以處理很多個連接配接請求。 如此,swoole就輕松的處理了高并發。
swoole如何實作異步I/O
swoole的worker程序有2種類型:一種是普通的worker程序,一種是task worker程序。
worker程序是用來處理普通的耗時不是太長的請求;task worker程序用來處理耗時較長的請求,比如資料庫的I/O操作。
我們以異步Mysql舉例:
耗時較長的Mysql查詢進入worker
|
worker通過管道将這個請求交給taskworker來處理
|
worker再去處理其他請求
|
task worker處理完畢後,處理結果通過管道傳回給worker
|
worker 将結果傳回給reactor
|
reactor将結果傳回給請求方
如此,通過worker、task worker結合的方式,我們就實作了異步I/O。
總結一下 swoole 的技術特點:
1 常駐記憶體,避免重複加載帶來的性能損耗,提升海量性能;
2 基于epoll,輕松支援高并發;
3 協程異步I/O,提高對I/O密集型場景并發處理能力;
4 支援多種通信協定,友善地開發 Http、WebSocket、TCP、UDP 等應用
3.swoole與php-fpm對比有哪些優缺點?
優點
1 常駐記憶體的 cli 運作模式,不用每次請求加載一次項目代碼
2 大大提高了對連接配接請求的并發能力
3 協程異步I/O,提高對I/O密集型場景并發處理能力
4 支援多種通信協定,能搭建 TCP/UDP/UnixSocket 伺服器
5 原生支援毫秒定時器
缺點
1 相關文檔較少
2 不支援 xdebug,不支援手動 dump,不熟悉相關工具的話,不太友善調試
3 入門難度高,多數 phper 不了解 TCP/IP 網絡協定、多程序 / 多線程、異步 io 等
3.程序的基本知識
對于一個程序來說,它的核心内容分為兩個部分,一個是它的記憶體,這個記憶體是這程序建立之初從系統配置設定的,它所有建立的變量都會存儲在這一片記憶體環境當中
一個是它的上下文環境我們知道程序是運作在作業系統的,那麼對于程式來說,它的運作依賴作業系統配置設定給它的資源,作業系統的一些狀态。
Swoole的程序結構
Swoole的高效不僅僅于底層使用c編寫,他的程序結構模型也使其可以高效的處理業務,我們想要深入學習,并且在實際的場景當中使用必須了解,下面我們先看一下結構圖
首先先介紹下swoole的這幾種程序分别是幹什麼的:
1)Master程序:主程序
第一層,Master程序,這個是swoole的主程序,這個程序是用于處理swoole的核心事件驅動的,那麼在這個程序當中可以看到它擁有一個MainReactor[線程]以及若幹個Reactor[線程],swoole所有對于事件的監聽都會在這些線程中實作,比如來自用戶端的連接配接,信号處理等。
MainReactor(主線程)
1 主線程會負責監聽server socket,如果有新的連接配接accept,
2 主線程會評估每個Reactor線程的連接配接數量。
3 将此連接配接配置設定給連接配接數最少的reactor線程,做一個負載均衡。
Reactor線程組
1 Reactor線程負責維護用戶端機器的TCP連接配接、處理網絡IO、收發資料
2 完全是異步非阻塞的模式。
3 swoole的主線程在Accept新的連接配接後,會将這個連接配接配置設定給一個固定的Reactor線程,
4 在socket可讀時讀取資料,并進行協定解析,将請求投遞到Worker程序。在socket可寫時将資料發送給TCP用戶端。
心跳包檢測線程(HeartbeatCheck)
1 Swoole配置了心跳檢測之後,心跳包線程會在固定時間内對所有之前線上的連接配接
2 發送檢測資料包
UDP收包線程(UdpRecv)
1 接收并且處理用戶端udp資料包
2)Manger程序:管理程序
Swoole在運作中會建立一個單獨的管理程序,所有的worker程序和task程序都是從管理程序Fork出來的。管理程序會監視所有子程序的退出事件,當worker程序發生緻命錯誤或者運作生命周期結束時,管理程序會回收此程序,并建立新的程序。換句話也就是說,對于worker、task程序的建立、回收等操作全權有“保姆”Manager程序進行管理。
再來一張圖梳理下Manager程序和Worker/Task程序的關系。
3)Worker程序:工作程序
worker 程序屬于swoole的主邏輯程序,使用者處理用戶端的一系列請求,接受由Reactor線程投遞的請求資料包,并執行PHP回調函數處理資料生成響應資料并發給Reactor線程,由Reactor線程發送給TCP用戶端可以是異步非阻塞模式,也可以是同步阻塞模式。
4)Task程序:異步任務工作程序
taskWorker程序這一程序是swoole提供的異步工作程序,這些程序主要用于處理一些耗時較長的同步任務,在worker程序當中投遞過來。
程序檢視及流程梳理
當啟動一個Swoole應用時,一共會建立2 + n + m個程序,2為一個Master程序和一個Manager程序,其中n為Worker程序數。m為TaskWorker程序數。
預設如果不設定,swoole底層會根據目前機器有多少CPU核數,啟動對應數量的Reactor線程和Worker程序。我機器為1核的。Worker為1。
是以現在預設我啟動了1個Master程序,1個Manager程序,和1個worker程序,TaskWorker沒有設定也就是為0,目前server會産生3個程序。
在啟動了server之後,在指令行執行ps -ajft|grep server.php檢視目前産生的程序
這三個程序中,所有程序的根程序,也就是例子中的21915程序,就是所謂的Master程序;而21917程序,則是Manager程序;最後的21919程序,是Worker程序。
swoole事件處理流程
swoole使用的是reactor事件處理模式,一個請求經曆的步驟如下
1 伺服器主線程等待用戶端連接配接。
2 Reactor線程處理接連socket,讀取socket上的請求資料(Receive),将請求封裝好後投遞給work程序。
3 Work程序就是邏輯單元,處理業務資料。
4 Work程序結果傳回給Reactor線程。
5 Reactor線程将結果寫回socket(Send)。