站长资讯网
最全最丰富的资讯网站

解析ThinkPHP5如何引入Go AOP和PHP AOP编程

项目背景

目前开发的WEB软件里有这一个功能,PHP访问API操作数据仓库,刚开始数据仓库小,没发现问题,随着数据越来越多,调用API时常超时(60s)。于是决定采用异步请求,改为60s能返回数据则返回,不能则返回一个异步ID,然后轮询是否完成统计任务。由于项目紧,人手不足,必须以最小的代价解决当前问题。

方案选择

  1. 重新分析需求,并改进代码
  2. 采用AOP方式改动程序

从新做需求分析,以及详细设计,并改动代码,需要产品,架构,前端,后端的支持。会惊动的人过多,在资源紧张的情况下是不推荐的。
采用AOP方式,不改动原有代码逻辑,只需要后端就能完成大部分任务了。后端用AOP切入请求API的方法,通过监听API返回的结果来控制是否让其继续运行原有的逻辑(API在60s返回了数据),或者是进入离线任务功能(API报告统计任务不能在60s内完成)。

之前用过AOP-PHP拓展,上手很简单,不过后来在某一个大项目中引入该拓展后,直接爆了out of memory,然后就研究其源码发现,它改变了语法树,并Hook了每个被调用的方法,也就是每个方法被调用是都会去询问AOP-PHP,这个方法有没有切面方法。所以效率损失是比较大的。而且这个项目距离现在已经有8年没更新了。所以不推荐该解决方案。

实际环境

Debian,php-fpm-7.0,ThinkPHP-5.10。

引入AOP

作为一门zui好的语言,PHP是不自带AOP的。那就得安装AOP-PHP拓展,当我打开pecl要下载时,傻眼了,全是bate版,没有显示说明支持php7。但我还是抱着侥幸心理,找到了git,发现4-5年没更新了,要不要等一波更新,哦,作者在issue里说了有时间就开始兼容php7。
好吧,狠话不多说,下一个方案:Go!AOP.看了下git,作者是个穿白体恤,喜欢山峰的大帅哥,基本每个issue都会很热心回复。

composer require goaop/framework

ThinkPHP5 对composer兼容挺不错的哦,(到后面,我真想揍ThinkPHP5作者)这就装好了,怎么用啊,git上的提示了简单用法。我也就照着写了个去切入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 核心服务配置

<?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钩子,让其启动 go-aop

<?PHP // file: ./application/tests/tags.php // 由于是thinkphp5.10 没有容器,所有需要在module下的tags.php文件里配置调用他  return [     // 应用初始化     'app_init'     => [],     // 应用开始     'app_begin'    => [],     // 模块初始化     'module_init'  => [         'app\tests\behavior\Bootstrap'     ],     // 操作开始执行     'action_begin' => [],     // 视图内容过滤     'view_filter'  => [],     // 日志写入     'log_write'    => [],     // 应用结束     'app_end'      => [], ];

兼容测试

好了,访问 http://127.0.0.1/tests/test1/… 显示:

hello

这不是预期的效果,在aspect定义了,访问该方法前,会输出方法的

赞(0)
分享到: 更多 (0)
网站地图   沪ICP备18035694号-2    沪公网安备31011702889846号