利用ThinkPHP+aws s3 来上传静态文件

总结归纳五月份工作采坑记,当初如果不坚持下来,现在可能还是在原地打转,所以我坚信我一步一个脚印的让自己过的更好!

原由

我们在日常工作中一般会遇到你的服务器被请求拖慢的情况,刚开始我也是茫茫然,不知所措,但是我是公司里面负责架构的人啦,不能坐视不管,所以拼命求救。

这里总结一下大部分的看法

  1. 你的服务器请求太多了,一个子域名在特定的时间接受的请求是有限的
  2. 你的服务器的I/O到达了瓶颈
  3. 你的静态资源把网络卡死了,一直在转圈圈

上面的所有的观点都指向了一个方案——动静分离

我刚开始也不知道啥是动静分离哈,于是乎百度一看,原来稍微大点的项目都要用到动静分离。据说还有稍微负载大点的连数据库都要分离呢,不光分离,还需要读写分离。

行动

有了理论基础,那就是开干呗。

  1. 在市场上找与我们公司匹配的分离方案
  2. 先把静态资源(上传的文件)分离到 aws s3中,我们的客户是国外居多
  3. 问高人分离的方案
  4. 动手开干

结果

我的最后方案是:

  1. 后台采用我娴熟的thinkPHP框架,这次是采用TP5.1,肯定会有小伙伴问我为毛不用TP6.0搞哇,这个破框架太扯,一年能给你弄出几个大版本出来,你追新?算是一个巨坑,关键是他不支持无缝升级。
  2. 静态资源,经过多方面的考虑,放置于 aws s3上,因为直接开通,不用向公司申请就能用
  3. 前台首次采用vue.js

因为我是负责后端的,所以我用这里只写后端的东西,记录一下,方便以后查阅和升级改造

首先、利用composer安装我们的aws sdk

composer require aws/aws-sdk-php

自动加载是thinkPHP帮忙处理了,这里不多说,那些刨根问底的人去www.kancloud.cn去看看,或许能得到更多。

其次、我跟着aws API文档编写一个工具类 aws.php

文件有注释,所有这里不做太多的解释

<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2019/5/11
 * Time: 8:47
 */

namespace app\common\helper;

use Aws\Exception\AwsException;
use Aws\Exception\MultipartUploadException;
use Aws\S3\S3Client;
use Aws\S3\MultipartUploader;
use think\facade\Config;

/**
 * Class Aws
 * @package app\common\helper
 */
class Aws
{
    protected $Bucket;

    /**
     *  最后修改日期:20190520  1314520  我就是那个爱你的pythoner 啊
     *  本方法 创建一个s3的客户端,app_key  option 都存储在app.config中
     *  具体写法 请参考 aws s3的php sdk 示例
     *  https://docs.aws.amazon.com/zh_cn/sdk-for-php/v3/developer-guide/getting-started_basic-usage.html
     * @return S3Client
     */
    public static function createClient()
    {
        $aws_app_key = Config::get('app.aws_app_key');
        $aws_options = Config::get('app.aws_options');
        $options = [
            'version' => $aws_options['version'],
            'region' => $aws_options['region'],
            'credentials' => [
                'key' => $aws_app_key['key'],
                'secret' => $aws_app_key['secret']
            ],
            'debug' => $aws_options['debug']
        ];
        $s3 = new S3Client($options);
        return $s3;
    }

    /**
     * @param $source
     * 源文件 上传文件 修改时间 20190517
     * 本方法 上传文件到s3 存储桶中
     * 需要两个参数 第一个参数是文件本身
     * 第二个是 文件名 也就是key
     * 该方法能将文件上传到亚马逊s3,如果成功返回文件在s3中的地址;
     * API参考 连接  MultipartUploader
     * https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html
     * @param $key
     * @return mixed|void
     */
    public static function uploader($source, $key)
    {
        $client = self::createClient();
        $uploader = new MultipartUploader($client, $source, [
            "bucket" => Config::get('app.aws_bucket'),
            "key" => $key,
        ]);
        do {
            try {
                $result = $uploader->upload()->get('Key');
            } catch (MultipartUploadException $exception) {
                rewind($source);
                $uploader = new MultipartUploader($client, $source, [
                    'state' => $exception->getState(),
                ]);
            }
        } while (!isset($result));
        return $result;
    }

    /***
     * 根据key 删除 本桶里的数据
     * 修改时间 : 20190520 1314520
     * 接收一个参数,就是需要删除的key
     * 如果这个文件存在,就删除,没有查询到这个文件就忽略掉不删除,防止在存储桶里删除了文件,系统里没有删除导致程序出错
     * API 参考  getObjectUrl   deleteObject
     * https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html
     * @param $key
     * @return bool
     */
    public static function deleteKey($key)
    {
        $Bucket = Config::get('app.aws_bucket');
        $client = self::createClient();
        try {
            $client->getObjectUrl($Bucket, $key); //有url
            try{
                $client->deleteObject(['Bucket' => $Bucket, 'Key' => $key,]);
                return true; //删除成功
            }catch (AwsException $exception){
                return false; //删除失败!
            }
        } catch (AwsException $exception) {
            return true; // 所查询的 KEY 不存在 不需要删除
        }
    }
}

有了这个类后,我们就可以use 他啊,很方便,我这里全部都定义了静态方法,在网上有看到他们的bucket \key 都放在这个文件里,我觉得不妥,我就提取出来放到了config里去了,所以这里你看不到真实的appkey bucket等信息

再次、我们就是调用该类,进行上传 和删除工作了是吧,这里提一个傻逼搞了一个错误2天才搞定,你知道是什么吗?哈哈。我在刚开始开发时把aws sdk的debug开启了,刚开始还蛮爽,都看到了所有的信息,但是后来,我要调用格式化数据是,发现这根本不是我要的数据,折腾了两天,结果把debug关闭就好了。我也是醉了。

<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2019/5/13
 * Time: 14:05
 */

namespace app\admin\controller;

use app\common\helper\Aws as AwsHelper;

/**
 * Class Aws
 * @package app\admin\controller
 */
class Aws extends Base
{
    public function uploader()
    {
        if (Request()->isGet()) {
            return "ok";
        }
        if (Request()->isPost()) {
            $soure = $this->request->file('file');
            $key = $soure->getInfo('name');
            $result = AwsHelper::uploader($soure, $key);
            if ($result) {
                return $result;
            }
        }
    }
}

问题

上传,该方法接受post请求,将文件接受到$soure中,上面 use AwsHelper类,下面就直接调用他里面的uploader方法,即可把文件上传到s3的桶里去。这里当然经历了很多曲折

  1. 开启那个傻逼模式的debug,关闭他
  2. 上传文件大于50M就傻缺了
    1. 修改php.ini 里的 upload_max_filesize 为300M,因为我这里的文件可能会在300M以下
    2. 修改nginx.conf 里 http段下面的 client_body_timeout 300s; client_max_body_size 300m;
  3. 上线到线上服务器后发现超时错误,和超过内存限制错误
    1. 修改php.ini里的几个参数
      1. memory_limit = 256M //这个参数后面发现可以视情况调整小一点,我们要根据服务器的配置来调整这个参数哈
      2. max_input_time = 900 //这个时间是我们上线后才发现有问题的,把这个调长一点就能好了,具体多长看情况
      3. max_execution = 300 //我这里 从他原来的90 调整到300
  4. 上传接收文件并上传是交给apache完成的,所以apache的配置也要修改,只不过他的这个修改在httpd-default.conf里
    1. 这个问题是关于上传到aws超时故障的,我们将Timeout 设置为3600
  5. 上线时发现thinkPHP 5.1 需要支持到PHP7以上才能工作
    1. 升级php5.6 到php-7.2.13
    2. 开启强制路由,用于防止一些安全事故发生的,我在Python 里写的都是基于路由的,tornado、flask里 都是基于路由的
    3. 在larval里 就明确地需要用路由映射才能使用

总结

  1. 开发过程中一定要写好故障处理指南,后面在其他项目中会减少跳坑动作
  2. 你写的文档会被别人看到,人家会来批判(喷水)你的编写,你可以从评论中学习其他高人的看法、
  3. 总结归纳你这个过程里的所有的坑,会让你更充实

下一步

新技术总是从边缘来到中心的,所以别以为现在我们学习的这些东西没啥用,或许哪天他就是主流。我兄弟袁普照就是这样。


参考文献

相关文章