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 协议》,转载必须注明作者和本文链接
本帖由系统于 6年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 10

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

7年前 评论

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

7年前 评论

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

7年前 评论

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

7年前 评论

我的 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

4年前 评论

@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": []
}
4年前 评论