摘要:框架組件化改造框架從單體應用到組件化改造的架構升級之路經過一年多的開發框架功能越來越完善也越來越復雜初創時期的單體應用已經無法支撐項目的快速發展于是開發組在年前為版制定了組件化改造的重構方案內容速覽組件化原理包管理基礎知識組件化方案來
date: 2018-3-21 13:22:16
title: Swoft| Swoft 框架組件化改造
description: Swoft 框架從單體應用到組件化改造的架構升級之路
經過一年多的開發, Swoft 框架功能越來越完善, 也越來越復雜. 初創時期的 單體應用, 已經無法支撐項目的快速發展, 于是開發組在年前為 1.0-beta 版制定了 組件化改造 的重構方案.
內容速覽:
組件化原理: PHP 包管理基礎知識
組件化方案: 來自 laravel/symfony 等成熟框架的組件化實現方案
Swoft 框架組件化實現
組件化原理編程始終要解決的一個問題: 代碼復用. 好的代碼, 基本要求是 正確, 能拿到預期的結果, 少 bug. 語言層的代碼復用解決方案, 通常稱之為 包管理(或者 依賴管理). 流行的編程語言, 都提供了很好的工具鏈對包管理的支持:
一個命令行工具, 用來 獲取/管理 包, 比如 php 的 composer, python 的 pip, js 的 npm, java 的 maven, go 的 go get
一個包管理的配置文件, 用來說明需要用到(依賴)的包, 比如 PHP 中 composer 使用 composer.josn, js 的 npm 使用 package.json
一個瀏覽包的網站, 用來查看包的信息, 比如 php 的 packgist, python 的 pypi 等
這樣, 當我們需要不同功能的時候, 就可以去查看是否有包已經提供了類似功能, 不用重復造輪子, 站在巨人的肩膀上.
回到 PHP 中, PHP中的包管理是如何實現的呢?
命名空間
首先需要知道的一個基礎概念, 是 命名空間. 引入命名空間是為了 解決同名沖突 -- 2 個包中有名字相同的類, 同時使用時就會出現類重復定義的提示. 使用命名空間后, 因為不同的包有不同的命名空間, 就不會出現沖突.
// 如果需要在同一個文件中使用相同名字的類, 使用別名 use AFar; use BFar as BFar;
自動加載 & PSR4
第二個需要知道的基礎概念, 是 自動加載. PHP 中最基礎(或者說最原始)的復用代碼的方法: include/include_one/require/require_once. 不過得益于 PHP 的 SPL庫 中的 spl_autoload_register() 方法, 現在有了更優雅的方式來復用代碼 -- 自動加載. 自動加載的規范也經歷了一段時間的升級與打磨, 最新的是 PSR4標準.
關于自動加載, 有一個很好的教程: 5-1 SPL使用spl_autoload_register函數裝載類 (10:03)composer 中的包管理
了解了基礎知識后, 就可以來掌握工具怎么用了. composer 中的包管理 根據 composer.json 文件中的 autoload / require / require-dev 配置項來管理.
autoload 定義自動加載, 項目自身的代碼, 也應該按照包管理的規范, 進行組織, 比如 Swoft 的 composer.json 配置文件:
... "autoload": { "psr-4": { "App": "app/" }, "files": [ "app/Swoft.php" ] }, ...
composer 支持多種方式的自動加載方式, 這里面有一定的歷史原因, 因為需要兼容一些 陳舊 的代碼. 現在比較常用的 2 種方式:
psr-4: PSR4 標準, 優先推薦的方式
files: 直接加載文件, 通常用來加載 幫助函數, 類似于 PHP 的 require 語法來代碼復用
require 標識需要依賴的包, 格式是 包名 - 版本限制 的鍵值對:
... "require": { "php": ">=7.0", "swoft/framework": "^1.0", "swoft/rpc": "^1.0", "swoft/rpc-server": "^1.0", "swoft/rpc-client": "^1.0", "swoft/http-server": "^1.0", "swoft/http-client": "^1.0", "swoft/task": "^1.0", "swoft/http-message": "^1.0", "swoft/view": "^1.0", "swoft/db": "^1.0", "swoft/cache": "^1.0", "swoft/redis": "^1.0", "swoft/console": "^1.0", "swoft/devtool": "^1.0", "swoft/session": "^1.0", "swoft/i18n": "^1.0", "swoft/process": "^1.0", "swoft/memory": "^1.0", "swoft/service-governance": "^1.0" }, ...
關于 版本控制 的知識, 以及 >= ^ ~ 等特殊字符, alpha beta dev dev-master 等標識, 只是約定俗成的一些定義, 了解清楚即可.
require-dev 標識開發環境需要依賴的包, 即正式環境不需要使用到的包, 比如單元測試等:
... "require-dev": { "eaglewu/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^5.7" }, ...
類似的, 還有 autoload-dev, 表示測試環境下使用到自動加載.
組件化方案: laravel 與 symfony 使用的方案參考 symfony 中的 composer.json 配置文件 和 laravel 中的 composer.json 配置文件, 會發現里面有一個配置項: replace.
replace 這個配置項, 在普通項目中很難看到, 卻是組件化改造中的重要配置, 它的定義如下:
使用項目中已有的包, 替換需要依賴的包
比如 symfony 中的 composer.json 配置文件:
... "replace": { "symfony/asset": "self.version", "symfony/browser-kit": "self.version", "symfony/cache": "self.version", "symfony/config": "self.version", "symfony/console": "self.version", "symfony/css-selector": "self.version", "symfony/dependency-injection": "self.version", ...
其中 "symfony/asset" 包, 有一個多帶帶的github 倉庫 symfony/asset, symfony 項目 本身也包含 "symfony/asset" 包, 使用 replace, symfony 就可以使用自身包含的包, 不用去多帶帶獲取.
這樣帶來的好處:
主包包含所有的子包, 使用時使用 replace 配置, 所有的修改和提交都在主包中進行
其他項目依舊可以使用 require, 多帶帶使用子包; 子包只接受來自主包分發來的代碼, 不接受在子包上的更改
Swoft 框架組件化實現Swoft 在 1.0-beta版中的依賴, Swoft 項目:
... "require": { "php": ">=7.0", "swoft/framework": "^1.0", "swoft/rpc": "^1.0", "swoft/rpc-server": "^1.0", "swoft/rpc-client": "^1.0", "swoft/http-server": "^1.0", "swoft/http-client": "^1.0", "swoft/task": "^1.0", "swoft/http-message": "^1.0", "swoft/view": "^1.0", "swoft/db": "^1.0", "swoft/cache": "^1.0", "swoft/redis": "^1.0", "swoft/console": "^1.0", "swoft/devtool": "^1.0", "swoft/session": "^1.0", "swoft/i18n": "^1.0", "swoft/process": "^1.0", "swoft/memory": "^1.0", "swoft/service-governance": "^1.0" }, ...
改造后, Swoft 項目, 主項目只用依賴 "swoft/framework":
... "require": { "php": ">=7.0", "swoft/framework": "^1.0" }, ...
"swoft/framework" 項目, 包含其他子包:
... "replace": { "swoft/rpc": "self.version", "swoft/rpc-server": "self.version", "swoft/rpc-client": "self.version", "swoft/http-server": "self.version", "swoft/http-client": "self.version", "swoft/task": "self.version", "swoft/http-message": "self.version", "swoft/view": "self.version", "swoft/db": "self.version", "swoft/cache": "self.version", "swoft/redis": "self.version", "swoft/console": "self.version", "swoft/devtool": "self.version", "swoft/session": "self.version", "swoft/i18n": "self.version", "swoft/process": "self.version", "swoft/memory": "self.version", "swoft/service-governance": "self.version" } ...
其中子項目聲明到主項目提交修改:
Report issues and send Pull Requests in the main Swoft repository
整個開發流程如下:
在 daydaygo/swoft-framework 項目 新建 component2 分支開發此次組件化改造
修改 Swoft 項目的 composer.json 文件, 快速獲取所有 Swoft 組件的 master 分支代碼:
"require": { "php": ">=7.0", "swoft/framework": "dev-master", "swoft/rpc": "dev-master", "swoft/rpc-server": "dev-master", "swoft/rpc-client": "dev-master", "swoft/http-server": "dev-master", "swoft/http-client": "dev-master", "swoft/task": "dev-master", "swoft/http-message": "dev-master", "swoft/view": "dev-master", "swoft/db": "dev-master", "swoft/cache": "dev-master", "swoft/redis": "dev-master", "swoft/console": "dev-master", "swoft/devtool": "dev-master", "swoft/session": "dev-master", "swoft/i18n": "dev-master", "swoft/process": "dev-master", "swoft/memory": "dev-master", "swoft/service-governance": "dev-master" },
復制各個組件的代碼到 swoft-framework 項目中, 修改 composer.json 的中的 autoload / replace 配置(具體修改點擊鏈接查看)
Swoft 各組件依賴關系圖: http://naotu.baidu.com/file/7...
提交 swoft-framework 代碼.
下面以推送 swoft-view 組件到對應倉庫中為例:
出于 github 網速的原因, 測試過程使用 gitee 來加速
推送子項目到相應的 github 倉庫中, 參考:
dflydev/git-subsplit: 封裝 git subtree 為 git subplite 命令, 方便使用
laravel 中使用 git subsplit 的示例](https://github.com/laravel/fr...
# 建立 gitee.com:daydaygo/swoft-framework 倉庫 git remote add gitee git@gitee.com:daydaygo/swoft-framework.git git push gitee component2 # 拆分 git subsplit init git@gitee.com:daydaygo/swoft-framework.git # 更多項目, 一次填寫即可 git subsplit publish --heads="component2" --no-tags view:git@gitee.com:daydaygo/swoft-view.git # 清除生成的臨時文件 rm .subsplit
這個拆分過程耗時較長, 拆分后的效果: gitee.com/daydaygo/swoft-view, gitee.com/daydaygo/swoft-framework
可以通過添加 github webhook 來做自動化, 具體請參考: dflydev/dflydev-git-subsplit-github-webhook
最后, 測試拆分后的代碼:
修改 swoft 項目的 composer.json 文件, 使用新版的 swoft-framework 項目
... // 現在只需要依賴 swoft/framework, 版本號要制定分支, composer 會默認給分支名帶上 dev- 前綴 "require": { "php": ">=7.0", "swoft/framework": "dev-component2" }, .... "repositories": { "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com" }, // 制定包的地址, 這里指向我的 giee 倉庫地址 "0": { "type": "vcs", "url": "https://gitee.com/daydaygo/swoft-framework" } } ...
# 刪除以前的依賴 rm -rf composer.lock vendor # 更新 composer install --no-dev
運行測試, 可以參考項目下 .travis.yml 文件
至此, 大功告成.
寫在最后對項目進行組件化拆分, 推送子包到不同 github 倉庫 這樣的需求, 也許只有寫一個大型框架才會遇到. 但這也是正是動手寫一個框架的樂趣所在. PHP 中的包管理的基礎知識一直感覺 游刃有余, 直到遇到新的問題, 提出新的挑戰, 才發現還有更多的天地. 愿你也能感受到這分技術的樂趣.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28459.html
摘要:歷時年多緊鑼密鼓的開發,以及愉快而忙碌的春節假期,期間數從到快破,碼云首頁推薦,作者和社區的大力支持,正式版終于要和大家見面。此次更新新增了大量特性在易用性代碼復用性能方面都有所提升。可以用于構建高性能的系統中間件基礎服務等等。 歷時 1 年多緊鑼密鼓的開發,以及愉快而忙碌的春節假期,期間 github star 數從 500 到快破 1k,碼云首頁推薦,Swoole作者 Rango ...
摘要:所以呢,為了節省我們的時間,官方提供了一個鏡像包,里面包含了運行環境所需要的各項組件我們只需要下載鏡像并新建一個容器,這個容器就提供了框架所需的所有依賴和環境,將宿主機上的項目掛載到鏡像的工作目錄下,就可以繼續我們的開發或生產工作了。 Swoft 首個基于 Swoole 原生協程的新時代 PHP 高性能協程全棧框架,內置協程網絡服務器及常用的協程客戶端,常駐內存,不依賴傳統的 PHP-...
摘要:所以呢,為了節省我們的時間,官方提供了一個鏡像包,里面包含了運行環境所需要的各項組件我們只需要下載鏡像并新建一個容器,這個容器就提供了框架所需的所有依賴和環境,將宿主機上的項目掛載到鏡像的工作目錄下,就可以繼續我們的開發或生產工作了。 Swoft 首個基于 Swoole 原生協程的新時代 PHP 高性能協程全棧框架,內置協程網絡服務器及常用的協程客戶端,常駐內存,不依賴傳統的 PHP-...
摘要:即異步非阻塞,,事件驅動。優雅的注解聲明,容器,嚴格遵循規范。鏡像的主要用途官方提供了基于的鏡像。鏡像中已安裝配置好運行的所需組件及依賴。修改鏡像的使得容器啟動時不同時啟動服務,這就不需要要求我們掛載的本地項目必須完全安裝好依賴了。 之前有寫過一篇 Docker 安裝部署 Swoft 的文章,但有些冗余混亂,故重寫作為教程的開篇。要不讀讀看? Swoft項目:https://gith...
摘要:即異步非阻塞,,事件驅動。優雅的注解聲明,容器,嚴格遵循規范。鏡像的主要用途官方提供了基于的鏡像。鏡像中已安裝配置好運行的所需組件及依賴。修改鏡像的使得容器啟動時不同時啟動服務,這就不需要要求我們掛載的本地項目必須完全安裝好依賴了。 之前有寫過一篇 Docker 安裝部署 Swoft 的文章,但有些冗余混亂,故重寫作為教程的開篇。要不讀讀看? Swoft項目:https://gith...
閱讀 2623·2023-04-26 00:07
閱讀 2432·2021-11-15 11:37
閱讀 639·2021-10-19 11:44
閱讀 2164·2021-09-22 15:56
閱讀 1717·2021-09-10 10:50
閱讀 1497·2021-08-18 10:21
閱讀 2565·2019-08-30 15:53
閱讀 1630·2019-08-30 11:11