异步处理一般需要接触消息队列,翻laravel源码找到一种实现伪异步的办法
源码分析
laravel入口文件public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php';/*|--------------------------------------------------------------------------| Run The Application|--------------------------------------------------------------------------|| Once we have the application, we can handle the incoming request| through the kernel, and send the associated response back to| the client's browser allowing them to enjoy the creative| and wonderful application we have prepared for them.|*/$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);//结合bootstrap/app.php中代码可知//$kernel=App\Http\Kernel::class$response = $kernel->handle($request = Illuminate\Http\Request::capture());//$response 实际上是Symfony\Component\HttpFoundation\Response$response->send();//send() 实际上执行了fastcgi_finish_request()$kernel->terminate($request, $response);//响应结束之后可以继续do someting...
Illuminate\Foundation\Http\Kernel.php
/*** Call the terminate method on any terminable middleware.** @param \Illuminate\Http\Request $request* @param \Illuminate\Http\Response $response* @return void*/public function terminate($request, $response){$this->terminateMiddleware($request, $response);//执行terminate中间件$this->app->terminate();}/*** Call the terminate method on any terminable middleware.** @param \Illuminate\Http\Request $request* @param \Illuminate\Http\Response $response* @return void*/protected function terminateMiddleware($request, $response){$middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge($this->gatherRouteMiddleware($request),$this->middleware);foreach ($middlewares as $middleware) {if (! is_string($middleware)) {continue;}[$name] = $this->parseMiddleware($middleware);$instance = $this->app->make($name);if (method_exists($instance, 'terminate')) {$instance->terminate($request, $response);//执行中间件中的terminate}}}
fastcgi_finish_request()把数据返回给客户端之后关闭连接,但是php进程仍然在执行。
利用这个特性可以在响应结束之后,进程结束之前做一些不重要的耗时任务,
当然如果耗时非常长还是应该使用真正的异步例如消息队列等,不然出现php-fpm无法及时关闭而进程数过多。fpm模型是另外一个话题这里就不展开叙述了
由上面源码可知中间件中定义terminate方法既可以完成伪异步
实现步骤
创建terminate事件
namespace App\Light\TerminateHandle;class TerminateEvent{//回调函数public $callback;public function __construct(callable $callback){$this->callback = $callback;}}
创建Handler
namespace App\Light\TerminateHandle;class Handler{//保存需要执行的事件public $events = [];/*** 处理事件*/public function terminate(){if (!empty($this->events)) {foreach ($this->events as $event) {call_user_func($event->callback);}}}/*** 添加事件* @param TerminateEvent $event*/public function add(TerminateEvent $event){$this->events[] = $event;}}
创建门面类
namespace App\Light\TerminateHandle;use Illuminate\Support\Facades\Facade;/*** 支持IDE语法提示* @method static \App\Light\TerminateHandle\Handler terminate()* @method static \App\Light\TerminateHandle\Handler add(TerminateEvent $event)*/class TerminateHandle extends Facade{protected static function getFacadeAccessor(){return 'TerminateHandle';}}
创建terminate中间件
namespace App\Light\TerminateHandle;class TerminateMiddleware{public function handle($request, \Closure $next){return $next($request);}/*** 响应结束之后,进程结束之前执行* @param $request* @param $response*/public function terminate($request, $response){TerminateHandle::terminate();}}
创建服务提供者
namespace App\Light\TerminateHandle;use Illuminate\Support\ServiceProvider;class TerminateHandleServiceProvider extends ServiceProvider{public function register(){//绑定单例$this->app->singleton('TerminateHandle', function () {return new Handler();});//注册中间件$kernel = $this->app[\Illuminate\Contracts\Http\Kernel::class];$kernel->pushMiddleware(TerminateMiddleware::class);}}
注册服务提供者
config/app.php中加入
//注册terminate middleware服务提供者App\Light\TerminateHandle\TerminateHandleServiceProvider::class,
使用方法
class WelcomeController extends Controller{public function index(){TerminateHandle::add(new TerminateEvent(function (){info('一个事件');}));TerminateHandle::add(new TerminateEvent(function (){info('另一个事件');}));return ['code' => 1];}}
