Commit 65ba2ba9 authored by Liu lu's avatar Liu lu
Browse files

异步协程与异步日志批处理

parent 478e0d4c
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
<?php


/**
 * 异步协程处理
 */
namespace Meibuyu\Micro\Annotation;

use Hyperf\Di\Annotation\AbstractAnnotation;

/**
 * @Annotation
 * @Target("METHOD")
 */
class AsyncCoroutine extends AbstractAnnotation
{

    public function collectMethod(string $className, ?string $target): void
    {
        parent::collectMethod($className, $target); // TODO: Change the autogenerated stub
    }
}
 No newline at end of file
+21 −0
Original line number Diff line number Diff line
<?php

/**
 * 日志追踪 写队列 批量更新到数据库
 */
namespace Meibuyu\Micro\Annotation;

use Hyperf\Di\Annotation\AbstractAnnotation;

/**
 * @Annotation
 * @Target("METHOD")
 */
class LogTrace extends AbstractAnnotation
{

    public function collectMethod(string $className, ?string $target): void
    {
        parent::collectMethod($className, $target); // TODO: Change the autogenerated stub
    }
}
 No newline at end of file
+47 −0
Original line number Diff line number Diff line
<?php
namespace Meibuyu\Micro\Aspect;

use Meibuyu\Micro\Handler\LogTrace\LogTraceHandler;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Meibuyu\Micro\Annotation\AsyncCoroutine;
use Hyperf\Utils\Coroutine;

/**
 * @Aspect(
 *   annotations={
 *      AsyncCoroutine::class
 *   }
 * )
 */
class AsyncCoroutineAspect extends AbstractAspect
{

    /**
     * 优先级
     * @var int
     */
    public $priority = 998;

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        // 切面切入后,执行对应的方法会由此来负责
        // $proceedingJoinPoint 为连接点,通过该类的 process() 方法调用原方法并获得结果
        // 在调用前进行某些处理
        return Coroutine::create(function ()use($proceedingJoinPoint){

            LogTraceHandler::recordProcess(
                '投递到子协程任务,id:'.Coroutine::id()
                .' ,类:'.$proceedingJoinPoint->className
                .' ,方法:'.$proceedingJoinPoint->methodName
                ,
                true
            );
            $result = $proceedingJoinPoint->process();
            LogTraceHandler::recordProcess($result,true);

        });

    }
}
 No newline at end of file
+34 −0
Original line number Diff line number Diff line
<?php
namespace Meibuyu\Micro\Aspect;

use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Meibuyu\Micro\Annotation\LogTrace;
use Meibuyu\Micro\Handler\LogTrace\LogTraceHandler;

/**
 * @Aspect(
 *   annotations={
 *      LogTrace::class
 *   }
 * )
 */
class LogTraceAspect extends AbstractAspect
{

    /**
     * 优先级
     * @var int
     */
    public $priority = 999;

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        LogTraceHandler::createLogTrace(
            $proceedingJoinPoint->className.'@'.$proceedingJoinPoint->methodName,
            $proceedingJoinPoint->getArguments()
        );
        return $proceedingJoinPoint->process();
    }
}
 No newline at end of file
+154 −0
Original line number Diff line number Diff line
<?php


/**
 * 执行日志记录
 */

namespace Meibuyu\Micro\Handler\LogTrace;

use Meibuyu\Micro\Model\LogTrace;
use Hyperf\Utils\Coroutine;
use Swoole\Server;
use Throwable;

/**
 * Class LogTraceHandler
 * @package App\Service
 */
class LogTraceHandler
{

    /**
     * 1.对执行操作进行的方法入口注解 LogTrace
       2.对程序主动进行输出
          try {
                //流程1
                LogTraceHandler::recordProcess('执行到流程1');

                //流程2
                LogTraceHandler::recordProcess('执行到流程2');

                //记录输出数组
                LogTraceHandler::recordProcess(['test'=>1]);

                //流程3 抛出一个异常
                throw new Exception('test111');

                //流程执行完成标记结束
                LogTraceHandler::markComplete();

            }catch (\Throwable $exception){
                //记录异常日志
                LogTraceHandler::recordProcess($exception);
        }
     * @param $params
     * @param $source
     * @return mixed
     * @throws \Exception
     */
    public static function createLogTrace($source, $params)
    {
        if(!Coroutine::inCoroutine()) return;

        LogTrace::insertOrIgnore([
            'request_id'            => self::getRequestId(),
            'origin_params' => json_encode($params),
            'source'        => $source,
            'created_at'    => now(),
            'process_info'  => ''
        ]);
    }


    /**
     * @param bool $isInAsyncCoroutine
     * @return string
     * @throws \Exception
     */
    private static function getRequestId($isInAsyncCoroutine=false)
    {
        $workId = posix_getpid();
        $cid = $isInAsyncCoroutine?Coroutine::parentId(Coroutine::id()):Coroutine::id();
        if(!$cid) throw new \Exception('无法使用协程标记录日志');
        return container(Server::class)->stats()['start_time'] .$workId. $cid;
    }

    /**
     * 程序执行完成标记结束
     * @throws \Exception
     */
    public static function markComplete()
    {
        if(!Coroutine::inCoroutine()) return;
        container(LogTraceQueue::class)->addToQueue([
            'request_id'=>self::getRequestId(),
            'process_info'=>'',
            'is_completed'=>YES
        ]);
        //LogTrace::where('request_id', self::getRequestId())->update(['is_completed' => YES]);
    }

    /*
     * 事务回滚导致部分流程日志无法记录 暂写到文件里
     * 待写到Es后可以避免
     * 记录当前日志(包括异常捕获)
     */
    public static function recordProcess($track,$isInAsyncCoroutine=false)
    {
        if (empty($track)) return;
        if(!Coroutine::inCoroutine()) return;

        $logInfo = '';
        if ($track instanceof Throwable) {
            $logInfo = $track->getMessage() . "\n" .
                $track->getFile() . " line:" .
                $track->getLine() . "\n" .
                $track->getTraceAsString();
        }

        if (is_array($track)) {
            $logInfo = var_export($track, true);
        }
        if (is_string($track)||is_numeric($track)) {
            $logInfo = $track;
        }
        $logInfo .=  "\n\n";

        container(LogTraceQueue::class)->addToQueue([
            'request_id'=>self::getRequestId($isInAsyncCoroutine),
            'process_info'=>$logInfo,
            'is_completed'=>NO
        ]);
//        $log = LogTrace::where('request_id', self::getRequestId())->first();
//        if(empty($log)) return ;
//
//        $log->update([
//            'process_info' => Db::raw("CONCAT(process_info,\"{$logInfo}\")")
//        ]);
//        //写入文件
//        put_log(
//            self::getRequestId()."\n".
//            $logInfo,
//            str_replace('\\','_',$log->source).'/'.today()
//        );


    }


//    /**
//     * 记录流程日志
//     * @param $funName
//     * @param $arguments
//     * @throws \Exception
//     */
//    public static function __callStatic($funName, $arguments)
//    {
//        if(self::$instance){
//            throw new \Exception('请用LogTraceService::createLogTrace 先实例化对象');
//        }
//        self::$instance->$funName($arguments);
//    }

}
 No newline at end of file
Loading