天天看點

Swoole引擎原理(swoole為什麼能提升PHP的速度)

首先,在網上找的資訊說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為什麼能提升PHP的速度)

Swoole的程序結構

Swoole的高效不僅僅于底層使用c編寫,他的程序結構模型也使其可以高效的處理業務,我們想要深入學習,并且在實際的場景當中使用必須了解,下面我們先看一下結構圖

Swoole引擎原理(swoole為什麼能提升PHP的速度)

首先先介紹下swoole的這幾種程序分别是幹什麼的:

1)Master程序:主程序

第一層,Master程序,這個是swoole的主程序,這個程序是用于處理swoole的核心事件驅動的,那麼在這個程序當中可以看到它擁有一個MainReactor[線程]以及若幹個Reactor[線程],swoole所有對于事件的監聽都會在這些線程中實作,比如來自用戶端的連接配接,信号處理等。

Swoole引擎原理(swoole為什麼能提升PHP的速度)

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程序的關系。

Swoole引擎原理(swoole為什麼能提升PHP的速度)

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檢視目前産生的程序

Swoole引擎原理(swoole為什麼能提升PHP的速度)

這三個程序中,所有程序的根程序,也就是例子中的21915程序,就是所謂的Master程序;而21917程序,則是Manager程序;最後的21919程序,是Worker程序。

swoole事件處理流程

Swoole引擎原理(swoole為什麼能提升PHP的速度)

swoole使用的是reactor事件處理模式,一個請求經曆的步驟如下

1 伺服器主線程等待用戶端連接配接。
2 Reactor線程處理接連socket,讀取socket上的請求資料(Receive),将請求封裝好後投遞給work程序。
3 Work程序就是邏輯單元,處理業務資料。 
4 Work程序結果傳回給Reactor線程。
5 Reactor線程将結果寫回socket(Send)。