剛才百度了一下swoft框架,官網打不開了,倉庫也暫停了。不由感慨。曾經和同事踩了許多坑使用此極其小眾的框架完成微服務項目。使用它的唯一目的就是提高程序性能(底層使用了協程),為此大家都學習了很多新知識,解決很多百度都百度不到的問題,趕上了一波docker微服務的潮流。更有同事搭建了k8s集群作為測試環境(相當復雜)。雖然團隊規模不大,但是這個項目做的可以說是相當規范了。值得欣慰的是項目性能達到了預期,堪稱精美。隨着疫情的沖擊,項目的商業價值漸漸沒落。在運行了兩年之后下線了,但沒有想到的是如今swoft看起來也是無法使用了。所以碼農翻身的作者說的對,我們應該把80%時間用在不變的基礎里。追求新技術可能是一種賭注。但不管怎么說,這個框架的設計初衷是非常理想化的,可能是使用的復雜性過高導致用戶太少,失去了生命力。即使只是曇花一現,它也曾閃過光!相信它的設計理念里還是有很多有價值可供未來借鑒的部分。
以下內容是轉載的,原文見:https://cloud.tencent.com/developer/article/1169328
基於swoole 4.0全新的PHP編程模式

上面是一段PHP代碼,其中2個函數的執行時間都是1秒,整段代碼執行完成需要2秒。要想將這種串行執行方式轉換為並行執行,在PHP中可以通過創建多進程來執行每個函數,單個進程執行單個函數,這樣在1秒鍾能就能執行完上面的代碼。雖然在Java中多線程應用很普遍,但是很可惜PHP並不支持多線程。

除開多線程和多進程,還有一種方式也能實現並行編程,那就是協程(Coroutine),這也是GO語言的重要特性。協程的並發量相對多線程和多進程要高出很多,同一個進程內可以創建幾十萬甚至上百萬個協程,且只占用少量的內存空間。線程和進程由操作系統調度,是非常昂貴的系統資源,創建過多的話,在上下文保存和進行切換的開銷上會很大。

這里將前面的代碼以協程的形式進行了重寫,執行的時候會創建兩個協程,執行時間為1秒。雖然執行效果和多進程或多線程一樣,但實現原理有所不同。協程中這兩個函數的執行基於一種自動讓出的機制,一旦執行函數遇到IO操作,就會自動讓出當前執行棧交由下一個函數執行,在IO完成之后再恢復協程棧。
PHP由於自身的天然缺陷無法支持多線程,所以我們繞開了它直接在swoft 4.0中實現了協程。但由於針對CPU密集操作只能利用到一個核,所以在使用協程的時候還是會利用多進程的方式來復用CPU的多核操作。
協程最大的好處在於能夠提供極大的並發,因為它僅占用內存,不存在進程/線程切換開銷,單個進程就可開啟50w個協程。
我們在swoft 1.0的時候采用的技術方案和node.js的異步回調一樣,在2.0的時候開始嘗試實現協程,但是存在一些缺陷——協程不能用在所有的函數上,只能用在一些已經預定好的函數上。這是由於PHP有一些動態的特性,比如將URL映射到一個類方法上,這種場景下執行2.0的協程程序就會崩潰。4.0的時候我們對此做了一些優化,基於微信開源的庫重新實現了協程方案,這時的協程就達到了在Go語言中的效果。

上面展示的就是PHP中使用協程的三種方式。左上的代碼通過循環的方式創建了10個協程,下面這段則是在協程中執行讀文件的操作,且內部還嵌套了兩個協程,它們之間是相互依賴的關系。右邊的代碼直接創建了3個協程,每個協程的執行邏輯都不一樣。
有了協程之后,就會涉及到如何管理協程或數據通信的問題。在Java多線程中,線程之間的通信可能會使用鎖或者數據結構的方式解決,在協程編程中一般使用的chan的方式管理。
協程是一個用戶態的線程,同一時間執行的協程只有一個。這一點和多線程不同,創建出來的多個線程都會並行執行。

左邊這段代碼是協程編程,它會讀取一個全局的數組,當協程1讀取數組的時候,協程2其實沒有運行,直到協程1遇到IO操作釋放了控制權,協程2才會恢復再去讀全局變量,這樣就完全不用加鎖了。
右邊是線程編程,可以看到如果程序要讀取全局臨界資源就一定要加鎖,要不斷的lock、unlock。
Chan有點類似隊列,不過它自帶了協程調度能力。
多線程讀取隊列時,會有生產者和消費者。在隊列內存占用過多無法再寫入的情況下,生產者還是會持續寫入,一般的解決方案是進行盲等,比如讓生產者sleep一段時間然后再去寫入。在隊列無數據可返回的情況下,一種方案是讓消費者盲等,CPU死循環去等待,不過這樣會占滿CPU。一般的方案是在發現無數據返回的時候sleep一段時間,之后再嘗試讀取。
協程編程中可以通過chan來完成協程調度。當生產者發現容量不足的時候會展示掛起當前協程,直到有消費者拿走一些數據之后才會喚醒這個協程。消費者的讀取機制也是一樣的,無可用數據時就掛起,一旦生產者push數據后再喚醒。
由於PHP的動態語言特性,所以可以向chan中push任意的PHP變量,無論是對象還是數組。Chan的底層基於引用計數管理,完全沒有內存拷貝,除了標量類型是直接復制之外,包括數組、對象這些復雜的數據結構都是用的引用計數管理。像Go語言一樣,我們也提供了chan::select用來對多個chan進行讀寫判讀。

協程框架swoft的介紹
Swoft是基於協程實現的web開發框架。它借鑒了spring Cloud做了完全組件化的實現,里面很多功能都是一個小的組件,當然也可以用自定義的組件替換內置的組件。該框架也提供了依賴注入、容器、連接池、AOP,除了應用在web領域之外,還能夠用在微服務上。

上面兩行命令分別是用來創建swoft工程和引入相關組件。
目前swoft支持3種服務器,swoft-http-srever 、swoft-websocket-server swoft-rpc-server。 第一個用來做主流的web應用程序,第二個是長連接通信服務,最后是微服務領域的RPC服務。

通過命令行腳本能夠直接啟用以上3種服務,這里也提供了一些常用的腳本工具。

Swoft參考Java的Spring框架,用了很多注解編程的方式。對於Web開發中的URL映射,可以直接通過注解的方式寫Route。能夠自動將URL映射到當前Controller方法中,URL中的參數也會自動帶入類方法中。
基於swoft協程框架進行PHP微服務治理
Swoft自帶了一些微服務常用的組件,包括服務注冊、熔斷、降級、負載均衡、接口多版本等。

Swoft的服務注冊與發現是基於Google開源的consul,要使用consul需要添加一些配置,定義服務提供方和接入方的key。然后在前面提到的命令行腳本調用RPC start就會自動將我們的服務器節點注冊到consul服務器中。

Swoft的接口聲明也是基於注解的方式,如上圖所示通過注解定義了service指向的服務以及調用的接口,調用的時候會映射到對應的方法。
swoft的熔斷機制中失敗超過一定次數,服務就關閉,成功的話,服務重新連接。

這段代碼是關於熔斷器的調用,首先確定熔斷器的名詞,正常情況下調用handler,失敗的話就調用fallback進行一些處理。