异步处理一般需要接触消息队列,翻laravel源码找到一种实现伪异步的办法

源码分析

laravel入口文件public/index.php

  1. $app = require_once __DIR__.'/../bootstrap/app.php';
  2. /*
  3. |--------------------------------------------------------------------------
  4. | Run The Application
  5. |--------------------------------------------------------------------------
  6. |
  7. | Once we have the application, we can handle the incoming request
  8. | through the kernel, and send the associated response back to
  9. | the client's browser allowing them to enjoy the creative
  10. | and wonderful application we have prepared for them.
  11. |
  12. */
  13. $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
  14. //结合bootstrap/app.php中代码可知
  15. //$kernel=App\Http\Kernel::class
  16. $response = $kernel->handle(
  17. $request = Illuminate\Http\Request::capture()
  18. );
  19. //$response 实际上是Symfony\Component\HttpFoundation\Response
  20. $response->send();
  21. //send() 实际上执行了fastcgi_finish_request()
  22. $kernel->terminate($request, $response);
  23. //响应结束之后可以继续do someting...

Illuminate\Foundation\Http\Kernel.php

  1. /**
  2. * Call the terminate method on any terminable middleware.
  3. *
  4. * @param \Illuminate\Http\Request $request
  5. * @param \Illuminate\Http\Response $response
  6. * @return void
  7. */
  8. public function terminate($request, $response)
  9. {
  10. $this->terminateMiddleware($request, $response);//执行terminate中间件
  11. $this->app->terminate();
  12. }
  13. /**
  14. * Call the terminate method on any terminable middleware.
  15. *
  16. * @param \Illuminate\Http\Request $request
  17. * @param \Illuminate\Http\Response $response
  18. * @return void
  19. */
  20. protected function terminateMiddleware($request, $response)
  21. {
  22. $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
  23. $this->gatherRouteMiddleware($request),
  24. $this->middleware
  25. );
  26. foreach ($middlewares as $middleware) {
  27. if (! is_string($middleware)) {
  28. continue;
  29. }
  30. [$name] = $this->parseMiddleware($middleware);
  31. $instance = $this->app->make($name);
  32. if (method_exists($instance, 'terminate')) {
  33. $instance->terminate($request, $response);//执行中间件中的terminate
  34. }
  35. }
  36. }

fastcgi_finish_request()把数据返回给客户端之后关闭连接,但是php进程仍然在执行。
利用这个特性可以在响应结束之后,进程结束之前做一些不重要的耗时任务,
当然如果耗时非常长还是应该使用真正的异步例如消息队列等,不然出现php-fpm无法及时关闭而进程数过多。fpm模型是另外一个话题这里就不展开叙述了
由上面源码可知中间件中定义terminate方法既可以完成伪异步

实现步骤

创建terminate事件

  1. namespace App\Light\TerminateHandle;
  2. class TerminateEvent
  3. {
  4. //回调函数
  5. public $callback;
  6. public function __construct(callable $callback)
  7. {
  8. $this->callback = $callback;
  9. }
  10. }

创建Handler

  1. namespace App\Light\TerminateHandle;
  2. class Handler
  3. {
  4. //保存需要执行的事件
  5. public $events = [];
  6. /**
  7. * 处理事件
  8. */
  9. public function terminate()
  10. {
  11. if (!empty($this->events)) {
  12. foreach ($this->events as $event) {
  13. call_user_func($event->callback);
  14. }
  15. }
  16. }
  17. /**
  18. * 添加事件
  19. * @param TerminateEvent $event
  20. */
  21. public function add(TerminateEvent $event)
  22. {
  23. $this->events[] = $event;
  24. }
  25. }

创建门面类

  1. namespace App\Light\TerminateHandle;
  2. use Illuminate\Support\Facades\Facade;
  3. /**
  4. * 支持IDE语法提示
  5. * @method static \App\Light\TerminateHandle\Handler terminate()
  6. * @method static \App\Light\TerminateHandle\Handler add(TerminateEvent $event)
  7. */
  8. class TerminateHandle extends Facade
  9. {
  10. protected static function getFacadeAccessor()
  11. {
  12. return 'TerminateHandle';
  13. }
  14. }

创建terminate中间件

  1. namespace App\Light\TerminateHandle;
  2. class TerminateMiddleware
  3. {
  4. public function handle($request, \Closure $next)
  5. {
  6. return $next($request);
  7. }
  8. /**
  9. * 响应结束之后,进程结束之前执行
  10. * @param $request
  11. * @param $response
  12. */
  13. public function terminate($request, $response)
  14. {
  15. TerminateHandle::terminate();
  16. }
  17. }

创建服务提供者

  1. namespace App\Light\TerminateHandle;
  2. use Illuminate\Support\ServiceProvider;
  3. class TerminateHandleServiceProvider extends ServiceProvider
  4. {
  5. public function register()
  6. {
  7. //绑定单例
  8. $this->app->singleton('TerminateHandle', function () {
  9. return new Handler();
  10. });
  11. //注册中间件
  12. $kernel = $this->app[\Illuminate\Contracts\Http\Kernel::class];
  13. $kernel->pushMiddleware(TerminateMiddleware::class);
  14. }
  15. }

注册服务提供者

config/app.php中加入

  1. //注册terminate middleware服务提供者
  2. App\Light\TerminateHandle\TerminateHandleServiceProvider::class,

使用方法

  1. class WelcomeController extends Controller
  2. {
  3. public function index()
  4. {
  5. TerminateHandle::add(new TerminateEvent(function (){
  6. info('一个事件');
  7. }));
  8. TerminateHandle::add(new TerminateEvent(function (){
  9. info('另一个事件');
  10. }));
  11. return ['code' => 1];
  12. }
  13. }