Swoole高效跟傳統的web開發有什麼區別?

来源:https://www.cnblogs.com/a609251438/archive/2019/12/12/12031529.html
-Advertisement-
Play Games

一、swoole的運行模式 Swoole高效跟傳統的web開發有什麼區別,除了傳統的LAMP/LNMP同步開發模式,swoole的非同步開發模式是怎麼樣的。 1.1、傳統web開發模式 PHP web開發採用的方式是LAMP/LNMP架構,即Linux、Nginx,Mysql和PHP。這裡以nginx ...


一、swoole的運行模式

Swoole高效跟傳統的web開發有什麼區別,除了傳統的LAMP/LNMP同步開發模式,swoole的非同步開發模式是怎麼樣的。

 

1.1、傳統web開發模式

PHP web開發採用的方式是LAMP/LNMP架構,即Linux、Nginx,Mysql和PHP。這裡以nginx來舉例,大致結構為:

當請求進入時,web server將請求轉交給PHP-FPM,PHP-FPM是一個進程池架構的FastCGI服務,內置PHP解釋器。FPM負責解釋執行PHP文件生成響應,最終返回給web server,展現至前端。PHP文件中實現了許多業務邏輯,包括Mysql和Nosql的訪問,調用第三方應用等等。

這樣的結構php-fpm和nginx的配合已經運行得足夠好,但是由於php-fpm本身是同步阻塞進程模型,在請求結束後釋放所有的資源(包括框架初始化創建的一系列對象),導致PHP進程“空轉”(創建<-->銷毀<-->創建)消耗大量的CPU資源,從而導致單機的吞吐能力有限。

每次請求處理的過程都意味著一次PHP文件解析,環境設置等不必要的耗時操作PHP進程處理完即銷毀,無法在PHP程式中使用連接池等技術實現性能優化。

1.2、Swoole運行模式

針對傳統架構的問題,swoole從PHP擴展出發,解決了上述問題,對於swoole的進程模型,我們剛剛已經學過了。

相比於傳統架構,Swoole進程模型最大的特點在於其多線程Reactor模式處理網路請求,使得其能輕鬆應對大量連接。

除此之外的優點還包括:

全非同步非阻塞,占用資源開銷小,程式執行效率高

程式運行只解析載入一次PHP文件,避免每次請求的重覆載入

1.3、使用swoole和傳統php開發的缺點


1、更難上手。這要求開發人員對於多進程的運行模式有更清晰的認識
2、更容易記憶體泄露。在處理全局變數,靜態變數的時候一定要小心,這種不會被GC清理的變數會存在整個生命周期中,如果沒有正確的處理,很容易消耗完所有的記憶體。在php-fpm下,php代碼執行完記憶體就會被完全釋放。

二、註解機制

一般而言,在編程屆中註解是一種和註釋平行的概念,在解釋註解之前我們需要先定義一下 註解 與 註釋 的區別:

註釋:給程式員看,幫助理解代碼,對代碼起到解釋、說明的作用。

註解:給應用程式看,註解往往充當著對代碼的聲明和配置的作用,為可執行代碼提供機器可用的額外信息,在特定的環境下會影響程式的執行。

框架可以基於這些元信息為代碼提供各種額外功能,本質上註解就是理解註解只是配置的另一種展現方式:

比如通過註解的方式實現許可權的控制,就比配置文件當中配置要更加的方便

比如利用註解的方式配置路由、配置定時任務

現有的基於swoole的框架很多都是基於註解開發的,所以我們需要對註解機制有瞭解,接下來利用代碼來實現下註解

二、容器

3.1、什麼是容器?

容器 就是一個巨大的工廠,用於存放和管理 對象的生命周期,並且能夠解決程式的依賴關係,實現解耦。

3.2簡單的通過代碼理解依賴註入

 1 /**
 2 * 耦合嚴重的寫法
 3 **/
 4 class db {
 5     public static function get_db() {
 6         return new mysqli('127.0.0.1','user','pass','dbname',3306);
 7     }
 8 }
 9 class post {
10     private $db;
11 public function __construct($db){
12    //假設資料庫驅動發生了變化呢?如果寫死只能直接改動代碼
13        $this->db =new mysqli('127.0.0.1','user','pass','dbname',3306);    }
14     public function get_post($id){
15         return $this->db->query('SELECT * FROM post WHERE id ='.$id);
16     }
17 }
18 $post = new post();
19 $post->get_post(12);
20 
21 /*
22 *依賴註入的方式
23 */
24 <?php
25 class db {
26     public static function get_db() {
27         return new mysqli('127.0.0.1','user','pass','dbname',3306);
28     }
29 }
30 class post {
31     private $db;
32     public function set_db(db $db){
33         $this->db = $db;
34     }
35     public function get_post($id){
36         return $this->db->query(select xx from xxx);
37     }
38 }
39 $post = new post();
40 $post->set_db( db::get_db() ); //註入post類依賴的資料庫連接對象,通過類名直接調用靜態方法get_db
41 $post->get_post(11);

 

當沒有Ioc/DI容器時

當有了IoC/DI的容器後,post類不再主動去創建db類了,如下圖所示:

 

依賴註入:在A類中使用了B類的實例時,B對象的構造不是在A類某個方法中初始化的,而是在A類外部初始化之後以B類的對象傳入進來。這個過程就是依賴註入。所需要的類通過參數的形式傳入的就是依賴註入。

依賴註入:在A類中使用了B類的實例時,B對象的構造不是在A類某個方法中初始化的,而是在A類外部初始化之後以B類的對象傳入進來。這個過程就是依賴註入。所需要的類通過參數的形式傳入的就是依賴註入。

控制反轉IoC(Inversion of Control)是說創建對象的控制權進行轉移,以前創建對象的主動權和創建時機是由自己把控的,而現在這種權力轉移到第三方,比如轉移交給了IOC容器,它就是一個專門用來創建對象的工廠,你要什麼對象,它就給你什麼對象,有了 IOC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IOC容器了,通過IOC容器來建立它們之間的關係,控制反轉意思是說將依賴類的控制權交出去,由主動變為被動。

3.3、為什麼說在swoole當中使用容器更有意義?

傳統的php框架沒有常駐記憶體,因此每次請求進來都需要把用到的類都實例化一次,每次實例化都需要申請記憶體,當請求處理完之後又需要釋放,具體請參看第一點,所以我們可以在server啟動的時候就把類實例化預先放到記憶體中,減入對象的創建時間。

一個簡單的bean容器

 1 class BeanFactory{
 2     private static $container=[];
 3     
 4     public static function set(string $name,callable $func){
 5         self::$container[$name]=$func;
 6     }
 7 
 8 
 9     public static function get(string $name){
10         if(isset(self::$container[$name])){
11             return (self::$container[$name])();
12         }
13         return null;
14     }
15 }

 

3.4、Swoole進程結構

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

首先先介紹下swoole的這幾種進程分別是乾什麼的

從這些層級的名字,我們先大概說一下,下麵這些層級分別是乾什麼的,做一個詳細的說明。

1、Master進程:主進程

2、Manger進程:管理進程

3、Worker進程:工作進程

4、Task進程:非同步任務工作進程

1、Master進程

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

每一個線程都有自己的用途,下麵多每個線程有一個瞭解

1.1、MainReactor(主線程)

主線程會負責監聽server socket,如果有新的連接accept,主線程會評估每個Reactor線程的連接數量。將此連接分配給連接數最少的reactor線程,做一個負載均衡。

1.2 、Reactor線程組

Reactor線程負責維護客戶端機器的TCP連接、處理網路IO、收發數據完全是非同步非阻塞的模式。

swoole的主線程在Accept新的連接後,會將這個連接分配給一個固定的Reactor線程,在socket可讀時讀取數據,併進行協議解析,將請求投遞到Worker進程。在socket可寫時將數據發送給TCP客戶端。

1.3、心跳包檢測線程(HeartbeatCheck)

Swoole配置了心跳檢測之後,心跳包線程會在固定時間內對所有之前線上的連接

發送檢測數據包

1.4、UDP收包線程(UdpRecv)

接收並且處理客戶端udp數據包

2、管理進程Manager

Swoole想要實現最好的性能必須創建出多個工作進程幫助處理任務,但Worker進程就必須fork操作,但是fork操作是不安全的,如果沒有管理會出現很多的僵屍進程,進而影響伺服器性能,同時worker進程被誤殺或者由於程式的原因會異常退出,為了保證服務的穩定性,需要重新創建worker進程。

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進程當中投遞過來。

5、client跟server的交互:

1、client請求到達 Main Reactor,Client實際上是與Master進程中的某個Reactor線程發生了連接。

2、Main Reactor根據Reactor的情況,將請求註冊給對應的Reactor

3、客戶端有變化時Reactor將數據交給worker來處理

4、worker處理完畢,通過進程間通信(比如管道、共用記憶體、消息隊列)發給對應的reactor。

5、reactor將響應結果發給相應的連接請求處理完成

示意圖:

一個更通俗的比喻,假設Server就是一個工廠,那Reactor就是銷售,接受客戶訂單。而Worker就是工人,當銷售接到訂單後,Worker去工作生產出客戶要的東西。而Task_Worker可以理解為行政人員,可以幫助Worker幹些雜事,讓Worker專心工作。

6、進程的綁定事件

Master進程內的回調函數

onStart   Server啟動在主進程的主線程回調此函數
onShutdown  此事件在Server正常結束時發生

 

Manager進程內的回調函數

onManagerStart 當管理進程啟動時調用它
onManagerStop  當管理進程結束時調用它
onWorkerError  當worker/task_worker進程發生異常後會在Manager進程內回調此函數

 

Worker進程內的回調函數

onWorkerStart  此事件在Worker進程/Task進程啟動時發生
onWorkerStop    此事件在worker進程終止時發生。
onConnect   有新的連接進入時,在worker進程中回調
onClose   TCP客戶端連接關閉後,在worker進程中回調此函數
onReceive 接收到數據時回調此函數,發生在worker進程中
onRequest   有新的連接進入時,在worker進程中回調
onPacket 接收到UDP數據包時回調此函數,發生在worker進程中
onFinish  當worker進程投遞的任務在task_worker中完成時,task進程會通過finish()方法將任務處理的結果發送給worker進程。
onWorkerExit  僅在開啟reload_async特性後有效。非同步重啟特性
onPipeMessage  當工作進程收到由 sendMessage 發送的管道消息時會觸發事件

 

Task進程內的回調函數
onTask   在task_worker進程內被調用。worker進程可以使用swoole_server_task函數向task_worker進程投遞新的任務
onWorkerStart  此事件在Worker進程/Task進程啟動時發生
onPipeMessage  當工作進程收到由 sendMessage 發送的管道消息時會觸發事件

 

3.5、swoole運行模式及熱重啟

Swoole之所以性能卓越,是因為Swoole減少了每一次請求載入PHP文件以及初始化的開銷。但是這種優勢也導致開發者無法像過去一樣,修改PHP文件,重新請求,就能獲取到新代碼的運行結果。如果需要新代碼開始執行,往往需要先關閉伺服器然後重啟,這樣才能使得新文件被載入進記憶體運行,這樣很明顯不能滿足開發者的需求。幸運的是,Swoole提供了這樣的功能。

在swoole中,我們可以向主進程發送各種不同的信號,主進程根據接收到的信號類型做出不同的處理。比如下麵這幾個

1、kill -SIGTERM master_pid 終止Swoole程式,一種優雅的終止信號,會待進程執行完當前程式之後中斷,而不是直接幹掉進程

2、kill -USR1 master_pid 重啟所有的Worker進程

3、kill -USR2|-12 master_pid 重啟所有的Task Worker進程

當USR1信號被髮送給Master進程後,Master進程會將同樣的信號通過Manager進程轉發Worker進程,收到此信號的Worker進程會在處理完正在執行的邏輯之後,釋放進程記憶體,關閉自己,然後由Manager進程重啟一個新的Worker進程。新的Worker進程會占用新的記憶體空間,重新載入文件。

具體場景:

如果是上線的項目,一臺繁忙的後端伺服器隨時都在處理請求,如果管理員通過kill進程方式來終止/重啟伺服器程式,可能導致剛好代碼執行到一半終止。

這種情況下會產生數據的不一致。如交易系統中,支付邏輯的下一段是發貨,假設在支付邏輯之後進程被終止了。會導致用戶支付了貨幣,但並沒有發貨,後果非常嚴重。

如何解決?

這個時候我們需要考慮如何平滑重啟server的問題了。所謂的平滑重啟,也叫“熱重啟”,就是在不影響用戶的情況下重啟服務,更新記憶體中已經載入的php程式代碼,從而達到對業務邏輯的更新。

swoole為我們提供了平滑重啟機制,我們只需要向swoole_server的主進程發送特定的信號,即可完成對server的重啟。

註意事項:

1、更新僅僅只是針對worker進程,也就是寫在master進程跟manger進程當中更新代碼並不生效,也就是說只有在onWorkerStart回調之後載入的文件,重啟才有意義。在Worker進程啟動之前就已經載入到記憶體中的文件,如果想讓它重新生效,只能關閉server再重啟。

2、直接寫在worker代碼當中的邏輯是不會生效的,就算發送了信號也不會,需要通過include方式引入相關的業務邏輯代碼才會生效

四、為什麼需要分散式服務

4.1、早期單體架構帶來的問題

單體架構在規模比較小的情況下工作情況良好,但是隨著系統規模的擴大,它暴露出來的問題也越來越多,主要有以下幾點:

1.複雜性逐漸變高

比如有的項目有幾十萬行代碼,各個模塊之間區別比較模糊,邏輯比較混亂,代碼越多複雜性越高,越難解決遇到的問題。

2.技術債務逐漸上升

公司的人員流動是再正常不過的事情,有的員工在離職之前,疏於代碼質量的自我管束,導致留下來很多坑,由於單體項目代碼量龐大的驚人,留下的坑很難被髮覺,這就給新來的員工帶來很大的煩惱,人員流動越大所留下的坑越多,也就是所謂的技術債務越來越多。

3.阻礙技術創新

比如以前的某個項目使用tp3.2寫的,由於各個模塊之間有著千絲萬縷的聯繫,代碼量大,邏輯不夠清楚,如果現在想用tp5來重構這個項目將是非常困難的,付出的成本將非常大,所以更多的時候公司不得不硬著頭皮繼續使用老的單體架構,這就阻礙了技術的創新。

4.無法按需伸縮

比如說推薦模塊是CPU密集型的模塊,而訂單模塊是IO密集型的模塊,假如我們要提升訂單模塊的性能,比如加大記憶體、增加硬碟,但是由於所有的模塊都在一個架構下,因此我們在擴展訂單模塊的性能時不得不考慮其它模塊的因素,因為我們不能因為擴展某個模塊的性能而損害其它模塊的性能,從而無法按需進行伸縮。

5.系統高可用性差

因為所有的功能開發最後都部署到同一個框架里,運行在同一個進程之中,一旦某一功能涉及的代碼或者資源有問題,那就會影響整個框架中部署的功能。

五、什麼是RPC?

RPC(Remote Procedure Call)—遠程過程調用,它是一種通過網路從遠程電腦程式上請求服務,而不需要瞭解底層網路技術的協議。

比如說兩台伺服器A,B,一個應用部署在A伺服器上,想要調用B伺服器上應用提供的函數/方法,由於不在一個記憶體空間,不能直接調用,就需要通過網路來表達調用的語義和傳達調用的數據,而這種方式就是rpc

5.1、為什麼需要RPC?

RPC 的主要功能目標是讓構建分散式計算(應用)更容易,在提供強大的遠程調用能力時不損失本地調用的語義簡潔性。為實現該目標,RPC 框架需提供一種透明調用機制讓使用者不必顯式的區分本地調用和遠程調用。

Call(“listServices”)->info();

rpc隱藏了通訊的細節,調用遠程的服務就像調用本地的代碼一樣,其調用協議通常包含傳輸協議和編碼協議。
傳輸協議: 可以是自定義的tcp協議,可以是http、websockect
編碼協議: 如基於文本編碼的 xml、 json,也有二進位編碼的 protobuf 、binpack 等。

5.2、使用什麼協議?

RPC是一個軟體結構概念,是構建分散式應用的理論基礎。就好比為啥你家可以用到發電廠發出 來的電?
是因為電是可以傳輸的。至於用銅線還是用鐵絲還是其他種類的導線,也就是用http還是用其他協議的問題了。這個要看什麼場景,對性能要求怎麼樣。

5.3、rpc就只是介面調用?

一個完善的rpc其實還包含另一塊內容,通信協議外還有“服務註冊發現”,錯誤重試,服務限流,服務調用的負載均衡等等,rpc是不僅僅是一套設計規範,還包含了服務治理。

5.4 實際操作

傳輸協議: TCP協議

編碼協議: json編碼

我的知乎專欄


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 類的基本特性內 approved 已批准 implemented 已實施 mandatory 強制性的 proposed 偍儀的 validated 已驗證 ...
  • 一、添加郵件頭,抄送等信息 1.mail["From"]表示發送者信息,包括姓名和郵件 2.mail["To"]表示接收者信息,包括姓名和郵件地址 3.mail["Subject"]表示摘要或者主題信息 from email.mime.text import MIMEText from email. ...
  • 山有山的高度,水有水的深度。人生就是一場修行,註定會經歷千迴百轉,方能遇到一生的摯愛,註定要經歷浮浮沉沉,才能領會生命的涵義。燃一盞心燈,照亮每一個黑暗的角落,微笑,是最美的詩行。 ...
  • 網上很多文章都推薦使用Ant下載編譯,但本地實踐中屢屢失敗,無法下載。 後來參考 https://blog.csdn.net/xiongyouqiang/article/details/78941077 總算把調試環境搭建完成。 以下文章幾乎完全copy上述網址,但稍作延展。 下載源碼 官網直接下載 ...
  • 事情的經過是這樣的: 又是奶茶,行吧行吧。 快點開工,爭取李大偉回來之前搞定。 李大偉說是6位數字密碼 那麼我們可以利用python生成全部的六位數字密碼 這樣,我們就生成了一個從000000到99999的密碼表。 並把它們存入到 passdict.txt 的文件中。 6位的密碼表就這麼大!!! 下 ...
  • Generate name based md5 UUID (version 3) 函數來源:fzaninotto/faker 地址:https://github.com/fzaninotto/Faker ...
  • 本文主要學習瞭如何在Eclipse里使用Tomcat伺服器。 ...
  • [toc] 1. 布隆過濾器的概念 布隆過濾器(Bloom Filter) 是由 Howard Bloom在1970年提出的 ,它具有很好的 ,被用來 ,即判定 兩種情況。如果檢測結果為是,該元素不一定在集合中;但如果檢測結果為否,該元素一定不在集合中,因此Bloom filter 。 2. 布隆過 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...