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