Laravel 源码解读:PHP artisan down

Laravel 的 php artisan down 命令通常是在我们维护的时候使用的,因为在执行这条命令的时候,我们的 laravel 应用就进入了维护模式,会出现一个类似于下面这个 Be Ringht Back 页面
down-1.jpg

源码在哪

还是一样,我们首先找到 artisan down 命令的源码所在,它位于 Illuminate\Foundation\Console\DownCommand

Tips:依然是可以直接使用编辑器搜索 DownCommand

解读

主体方法还是 fire():

public function fire()
    {
        file_put_contents(
            $this->laravel->storagePath().'/framework/down',
            json_encode($this->getDownFilePayload(), JSON_PRETTY_PRINT)
        );

        $this->comment('Application is now in maintenance mode.');
    }

这里的代码其实非常简单,就是使用 file_put_contents()json_encode($this->getDownFilePayload() 内容写入storage/framework/down 文件中,这个文件很重要!storagePath() 就是位于 Illuminate\Foundation\Application 中的:

 public function storagePath()
    {
        return $this->storagePath ?: $this->basePath.DIRECTORY_SEPARATOR.'storage';
    }

也就是项目目录的 storage/ 文件路径。

写入的内容是什么?

文件内容是由 $this->getDownFilePayload() 生成,其实很简单的,就是在 DownCommand 中:

 protected function getDownFilePayload()
    {
        return [
            'time' => Carbon::now()->getTimestamp(),
            'message' => $this->option('message'),
            'retry' => $this->getRetryTime(),
        ];
    }

这里只返回一个数组,记录三个关键的信息:time , message, retry;这样看来其实我们可以自定义 Be Right Back 的字样的吧,也可以定义提示的时间,我们可以这样验证:
down-2.jpg
然后这样使用:

怎么判断是否是维护模式

超级简单,就在 Laravel 的核心类 Illuminate\Foundation\Application 中,启动之前先检查是否是维护模式 isDownForMaintenance()

 public function isDownForMaintenance()
    {
        return file_exists($this->storagePath().'/framework/down');
    }

你看,简单吧!它就是直接检测 storage/framework/down 是否存在!Neat!

最后

总结就是,执行 php artisan down 命令的时候,主要是生成 storage/framework/down 文件,包含了 time message 和 retry 三个关键信息,然后在 Laravel 启动的时候检测 storage/framework/down 文件是否存在就可以了。如果存在该文件,那就认为项目处于维护状态。

广告时间

坚持每天更新的公众号 codecasts,最近也在送书!有兴趣的可以关注一下
wechat_small.jpg

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 10

这个功能相对api应用来说比较鸡肋

6年前 评论

@jacobsun 这个不应该是 api 的应用场景

6年前 评论

@JellyBool 会有需求因为某些原因临时维护服务器呀,php artisan down 其实也是这个需求 不过只考虑到了 MVC 的场景 没有考虑到 API 的场景... 那这时候只能自己实现 还要 php artisan down 不就是鸡肋了嘛

6年前 评论

楼方这个字体是啥 ide的字段看起来 蛮苗条的

6年前 评论

我的 laravel 项目是api场景,我在中间件CheckForMaintenanceMode里找到一个 MaintenanceModeException异常类,所以想到了在App\Exceptions\Handler中这样写

use Illuminate\Foundation\Http\Exceptions\MaintenanceModeException;

if ($e instanceof MaintenanceModeException) {
            return response()->json([
                'code'    => '1000',
                'status'  => 'error',
                'message' => $e->getMessage(),
            ], 400);
}

这样前端拿到code 为1000时就知道api暂时不能用了,刚接触laravel,也不知道这样写合不合适,目前实现效果倒是可以了

file

3年前 评论

@renqun Laravel8 在默认情况下,执行 php artisan down 以后,不论访问 WEB 还是 API 都不再抛出 MaintenanceModeException 异常,而是 HttpException 异常。这时我们访问 API 就会报错:

// api 请求结果:
{
    "message": "Service Unavailable",
    // 看这里是 HttpException
    "exception": "Symfony\\Component\\HttpKernel\\Exception\\HttpException",
    "file": "/var/www/learn-laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
    "line": 79,
    "trace": [
        {
            "file": "/var/www/learn-laravel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
            "line": 167,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
            "type": "->"
        },
       .
       .
    ]
}

这样的结果对于客户端显然是不能忍的 :hammer: 。那么如何能给客户端返回正确的 JSON 格式数据呢?

解决办法是:在 App\Http\Middleware\PreventRequestsDuringMaintenance 中间件里做手脚,因为这个文件在 App\Http\Kernel 中已经被引入到了全局的中间件数组中。“全局的中间件”意思就是不论访问 WEB 还是 API 都会把这个数组中的中间件走一遍。

我们可以在 handle() 方法中,检测 uri 是否为 api 开头。是的话就返回一个正常的 json 响应,不是的话,就由父类继续执行:

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;

class PreventRequestsDuringMaintenance extends Middleware
{
    public function handle($request, \Closure $next)
    {
        // $uri = $request->path();

        if($this->app->isDownForMaintenance() && $request->is('api/*')){
          return response()->json([
            'code' => '4444',
            'message' => '服务器维护中...',
            'data' => [],
          ]);
        }

        return parent::handle($request, $next);
    }
}

这样,所有的 API 请求都会返回正常的 json 格式数据了:

// api 请求结果:
{
    "code": "4444",
    "message": "服务器维护中...",
    "data": []
}
3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!