項(xiàng)目背景
目前開發(fā)的WEB軟件里有這一個(gè)功能,PHP訪問API操作數(shù)據(jù)倉庫,剛開始數(shù)據(jù)倉庫小,沒發(fā)現(xiàn)問題,隨著數(shù)據(jù)越來越多,調(diào)用API時(shí)常超時(shí)(60s)。于是決定采用異步請(qǐng)求,改為60s能返回?cái)?shù)據(jù)則返回,不能則返回一個(gè)異步ID,然后輪詢是否完成統(tǒng)計(jì)任務(wù)。由于項(xiàng)目緊,人手不足,必須以最小的代價(jià)解決當(dāng)前問題。
方案選擇
- 重新分析需求,并改進(jìn)代碼
- 采用AOP方式改動(dòng)程序
從新做需求分析,以及詳細(xì)設(shè)計(jì),并改動(dòng)代碼,需要產(chǎn)品,架構(gòu),前端,后端的支持。會(huì)驚動(dòng)的人過多,在資源緊張的情況下是不推薦的。
采用AOP方式,不改動(dòng)原有代碼邏輯,只需要后端就能完成大部分任務(wù)了。后端用AOP切入請(qǐng)求API的方法,通過監(jiān)聽API返回的結(jié)果來控制是否讓其繼續(xù)運(yùn)行原有的邏輯(API在60s返回了數(shù)據(jù)),或者是進(jìn)入離線任務(wù)功能(API報(bào)告統(tǒng)計(jì)任務(wù)不能在60s內(nèi)完成)。
之前用過AOP-PHP
拓展,上手很簡(jiǎn)單,不過后來在某一個(gè)大項(xiàng)目中引入該拓展后,直接爆了out of memory
,然后就研究其源碼發(fā)現(xiàn),它改變了語法樹,并Hook了每個(gè)被調(diào)用的方法,也就是每個(gè)方法被調(diào)用是都會(huì)去詢問AOP-PHP
,這個(gè)方法有沒有切面方法。所以效率損失是比較大的。而且這個(gè)項(xiàng)目距離現(xiàn)在已經(jīng)有8年沒更新了。所以不推薦該解決方案。
實(shí)際環(huán)境
Debian,php-fpm-7.0,ThinkPHP-5.10。
引入AOP
作為一門zui好的語言,PHP是不自帶AOP的。那就得安裝AOP-PHP拓展,當(dāng)我打開pecl要下載時(shí),傻眼了,全是bate版,沒有顯示說明支持php7。但我還是抱著僥幸心理,找到了git,發(fā)現(xiàn)4-5年沒更新了,要不要等一波更新,哦,作者在issue里說了有時(shí)間就開始兼容php7。
好吧,狠話不多說,下一個(gè)方案:Go!AOP.看了下git,作者是個(gè)穿白體恤,喜歡山峰的大帥哥,基本每個(gè)issue都會(huì)很熱心回復(fù)。
composer require goaop/framework
ThinkPHP5 對(duì)composer兼容挺不錯(cuò)的哦,(到后面,我真想揍ThinkPHP5作者)這就裝好了,怎么用啊,git上的提示了簡(jiǎn)單用法。我也就照著寫了個(gè)去切入controller。
<?PHP namespace apptestscontroller; use thinkController; class Test1 extends Controller { public function test1() { echo $this->aspectAction(); } public function aspectAction() { return 'hello'; } }
定義aspect
<?PHP namespace apptestsaspect; use GoAopAspect; use GoAopInterceptFieldAccess; use GoAopInterceptMethodInvocation; use GoLangAnnotationAfter; use GoLangAnnotationBefore; use GoLangAnnotationAround; use GoLangAnnotationPointcut; use apptestscontrollerTest1; class MonitorAspect implements Aspect { /** * Method that will be called before real method * * @param MethodInvocation $invocation Invocation * @Before("execution(public|protected apptestscontrollerTest1->aspectAction(*))") */ public function beforeMethodExecution(MethodInvocation $invocation) { $obj = $invocation->getThis(); echo 'Calling Before Interceptor for method: ', is_object($obj) ? get_class($obj) : $obj, $invocation->getMethod()->isStatic() ? '::' : '->', $invocation->getMethod()->getName(), '()', ' with arguments: ', json_encode($invocation->getArguments()), "<br>n"; } }
啟用aspect
<?PHP // file: ./application/tests/service/ApplicationAspectKernel.php namespace apptestsservice; use GoCoreAspectKernel; use GoCoreAspectContainer; use apptestsaspectMonitorAspect; /** * Application Aspect Kernel * * Class ApplicationAspectKernel * @package apptestsservice */ class ApplicationAspectKernel extends AspectKernel { /** * Configure an AspectContainer with advisors, aspects and pointcuts * * @param AspectContainer $container * * @return void */ protected function configureAop(AspectContainer $container) { $container->registerAspect(new MonitorAspect()); } }
go-aop 核心服務(wù)配置
<?PHP // file: ./application/tests/behavior/Bootstrap.php namespace apptestsbehavior; use thinkException; use ComposerAutoloadClassLoader; use GoInstrumentTransformerFilterInjectorTransformer; use GoInstrumentClassLoadingAopComposerLoader; use DoctrineCommonAnnotationsAnnotationRegistry; use apptestsserviceApplicationAspectKernel; use apptestsThinkPhpLoaderWrapper; class Bootstrap { public function moduleInit(&$params) { $applicationAspectKernel = ApplicationAspectKernel::getInstance(); $applicationAspectKernel->init([ 'debug' => true, 'appDir' => __DIR__ . './../../../', 'cacheDir' => __DIR__ . './../../../runtime/aop_cache', 'includePaths' => [ __DIR__ . './../../tests/controller', __DIR__ . './../../../thinkphp/library/think/model' ], 'excludePaths' => [ __DIR__ . './../../aspect', ] ]); return $params; } }
配置模塊init鉤子,讓其啟動(dòng) go-aop
<?PHP // file: ./application/tests/tags.php // 由于是thinkphp5.10 沒有容器,所有需要在module下的tags.php文件里配置調(diào)用他 return [ // 應(yīng)用初始化 'app_init' => [], // 應(yīng)用開始 'app_begin' => [], // 模塊初始化 'module_init' => [ 'app\tests\behavior\Bootstrap' ], // 操作開始執(zhí)行 'action_begin' => [], // 視圖內(nèi)容過濾 'view_filter' => [], // 日志寫入 'log_write' => [], // 應(yīng)用結(jié)束 'app_end' => [], ];
兼容測(cè)試
好了,訪問 http://127.0.0.1/tests/test1/… 顯示:
hello
這不是預(yù)期的效果,在aspect定義了,訪問該方法前,會(huì)輸出方法的
相關(guān)推薦
- 2025年國內(nèi)免費(fèi)AI工具推薦:文章生成與圖像創(chuàng)作全攻略
- 從流量變現(xiàn)到信任變現(xiàn):個(gè)人站長(zhǎng)的私域運(yùn)營(yíng)方法論
- AI時(shí)代,個(gè)人站長(zhǎng)如何用AI工具實(shí)現(xiàn)“一人公司”
- 個(gè)人站長(zhǎng)消亡論?從“消失”到“重生”的三大破局路徑
- raksmart法蘭克福云服務(wù)器延遲高嗎?
- 自媒體人搭建直播服務(wù)器使用raksmart優(yōu)化技巧
- 華納云高防服務(wù)器3.6折起低至1188元/月,企業(yè)級(jí)真實(shí)防御20G`T級(jí),自營(yíng)機(jī)房一手服務(wù)器資源
- 服務(wù)器的系統(tǒng)和普通電腦系統(tǒng)一樣嗎?