一、为什么要给ThinkPHP的静态资源加CDN加速

咱们做网站或者小程序的时候,肯定会用到一堆静态资源,比如页面上的图片、样式文件、JS脚本、字体包这些。要是这些资源都从自己的服务器拉,会有俩大问题:一是离服务器远的用户加载慢,比如你服务器在广东,新疆的用户打开页面,光加载静态资源就得等好几秒;二是自己的服务器带宽不够用,要是突然有很多人访问,服务器带宽被占满,直接就卡得动不了。

CDN就是专门解决这俩问题的,简单说就是把你的静态资源复制到全国各地甚至全世界的节点服务器上,用户访问的时候,会自动选离他最近的节点拉资源,既快又能帮自己的服务器分担压力。

二、ThinkPHP静态资源的CDN配置步骤

2.1 先准备CDN的基础配置

首先你得先有一个CDN服务,现在国内的阿里云、腾讯云、七牛云都有免费的额度,够个人开发者用。开通之后,会给你一个CDN的域名,比如你自己的网站域名是www.abc.com,CDN域名可能是cdn.abc.com。

接下来要配置ThinkPHP,让它生成静态资源的链接时,自动把前缀换成CDN域名。ThinkPHP的配置文件在config/app.php里,咱们加一个CDN的配置项。

技术栈:ThinkPHP 6.0(所有示例都用这个版本)

<?php
return [
    // 其他原有配置项...
    // 新增CDN配置
    'cdn_domain' => 'https://cdn.abc.com', // 你的CDN域名,要带协议(http或https)
];

2.2 改造静态资源的输出方式

原来咱们在视图里输出静态资源,可能是直接写路径,比如<img src="/uploads/avatar/123.jpg">,或者用系统的url()函数生成,比如{:url('/uploads/avatar/123.jpg')}。现在要把这些都改成带CDN前缀的。

这里有两种常用的方法,第一种是直接修改视图里的输出,第二种是重写ThinkPHP的url()函数。

2.2.1 直接修改视图输出

如果是少数页面的静态资源,直接改就行,比如原来的样式引入:

<!-- 原来的写法 -->
<link rel="stylesheet" href="/static/css/style.css">
<script src="/static/js/jquery.min.js"></script>
<img src="/uploads/avatar/123.jpg">

<!-- 改成带CDN的写法 -->
<link rel="stylesheet" href="{$Think.config.app.cdn_domain}/static/css/style.css">
<script src="{$Think.config.app.cdn_domain}/static/js/jquery.min.js"></script>
<img src="{$Think.config.app.cdn_domain}/uploads/avatar/123.jpg">

这里的{$Think.config.app.cdn_domain}是ThinkPHP模板里输出配置项的语法,会自动替换成你之前配置的CDN域名。

2.2.2 重写url函数批量处理

如果你的项目里静态资源太多,一个个改视图太麻烦,就可以重写ThinkPHP的url()函数,让它自动给静态资源加CDN前缀。

首先在app/common.php里重写url()函数,这个文件是ThinkPHP的公共函数文件,会自动加载:

<?php
// 重写系统的url函数,用于给静态资源加CDN前缀
function url(string $url = '', array $vars = [], bool $suffix = true, ?bool $domain = false): string
{
    // 调用原来的url函数生成基础路径
    $originalUrl = \think\facade\Route::buildUrl($url, $vars, $suffix, $domain);
    
    // 判断是不是静态资源路径:以/static/或/uploads/开头的,都是静态资源
    if (str_starts_with($url, '/static/') || str_starts_with($url, '/uploads/')) {
        // 拼接CDN域名,注意要去掉原来路径开头的斜杠,避免出现双斜杠
        $cdnDomain = config('app.cdn_domain');
        $originalUrl = ltrim($originalUrl, '/');
        return $cdnDomain . '/' . $originalUrl;
    }
    
    // 不是静态资源的,直接返回原来的链接(比如后台的接口、页面链接)
    return $originalUrl;
}

这样改完之后,原来所有用{:url('/static/css/style.css')}生成的静态资源链接,都会自动变成https://cdn.abc.com/static/css/style.css,不用改视图里的代码。

2.3 配置CDN的回源规则

CDN节点上的资源不是凭空来的,得从你的源站(就是你自己的服务器)拉取。所以要给CDN配置回源规则,告诉CDN:当用户访问https://cdn.abc.com/static/css/style.css的时候,去哪个地址拉取这个文件。

一般CDN的回源规则配置很简单,登录CDN的控制台,找到你刚创建的域名,配置回源地址为你的源站域名(比如https://www.abc.com),回源端口默认是80或443,和源站的端口一致就行。

三、静态资源的版本刷新策略

用了CDN之后,会遇到一个很头疼的问题:你改了静态资源,比如把style.css里的样式改了,但是用户打开页面的时候,还是加载原来的旧版本,因为CDN节点会缓存这个文件,不会主动去源站拉新的。

这就需要版本刷新策略,目的是让用户能及时拿到最新的静态资源,同时又能尽可能利用CDN的缓存,提高加载速度。

3.1 为什么要做版本刷新

举个例子,你原来的style.css内容是body{color:red;},CDN节点把这个文件缓存了,缓存时间假设是7天。过了一天你改了样式,把颜色改成蓝色,但是用户访问的时候,CDN节点还是给用户返回缓存的红色版本,用户看不到更新后的效果。

版本刷新就是解决这个问题的,常用的有两种策略:文件名加版本号、查询参数加版本号。

3.2 策略一:文件名加版本号(推荐)

这种方法是把静态资源的文件名改成带版本号的,比如原来的style.css改成style.v1.0.0.css,或者style.20240520.css(用日期当版本号)。

具体实现步骤:

  1. 先在config/app.php里加一个版本号的配置:
<?php
return [
    // 其他原有配置...
    'cdn_domain' => 'https://cdn.abc.com',
    // 新增版本号配置,每次更新静态资源的时候改这个值
    'static_version' => 'v1.0.0',
];
  1. 重写之前的url()函数,让它自动给静态资源的文件名加版本号:
<?php
function url(string $url = '', array $vars = [], bool $suffix = true, ?bool $domain = false): string
{
    $originalUrl = \think\facade\Route::buildUrl($url, $vars, $suffix, $domain);
    
    if (str_starts_with($url, '/static/') || str_starts_with($url, '/uploads/')) {
        $cdnDomain = config('app.cdn_domain');
        $version = config('app.static_version');
        
        // 把路径拆成目录和文件名,比如把/static/css/style.css拆成/static/css/和style.css
        $pathParts = pathinfo($originalUrl);
        // 拼接带版本号的文件名,比如style.css变成style.v1.0.0.css
        $newFilename = $pathParts['filename'] . '.' . $version . '.' . $pathParts['extension'];
        // 重新拼接完整的路径
        $newUrl = $pathParts['dirname'] . '/' . $newFilename;
        
        $newUrl = ltrim($newUrl, '/');
        return $cdnDomain . '/' . $newUrl;
    }
    
    return $originalUrl;
}

这样改完之后,{:url('/static/css/style.css')}生成的链接就会变成https://cdn.abc.com/static/css/style.v1.0.0.css。

当你修改了静态资源之后,只要把config/app.php里的static_version改成v1.0.1,所有静态资源的链接就会变成新的版本号,用户访问的时候,CDN节点发现这个新的版本号文件没有缓存,就会去源站拉取最新的版本,用户就能看到更新后的效果了。

这种方法的好处是:不同版本的静态资源是完全不同的文件名,CDN会把它们当成不同的文件缓存,不会出现缓存冲突的问题,适合需要长期保留旧版本资源的场景(比如旧版APP还是用旧版本的静态资源)。

3.3 策略二:查询参数加版本号

这种方法是在静态资源的链接后面加一个查询参数,比如style.css?v=v1.0.0,实现起来更简单。

修改url()函数:

<?php
function url(string $url = '', array $vars = [], bool $suffix = true, ?bool $domain = false): string
{
    $originalUrl = \think\facade\Route::buildUrl($url, $vars, $suffix, $domain);
    
    if (str_starts_with($url, '/static/') || str_starts_with($url, '/uploads/')) {
        $cdnDomain = config('app.cdn_domain');
        $version = config('app.static_version');
        
        $originalUrl = ltrim($originalUrl, '/');
        // 拼接查询参数,注意如果原来的路径已经有参数,要加&,没有的话加?
        $separator = str_contains($originalUrl, '?') ? '&' : '?';
        return $cdnDomain . '/' . $originalUrl . $separator . 'v=' . $version;
    }
    
    return $originalUrl;
}

这样改完之后,{:url('/static/css/style.css')}生成的链接就会变成https://cdn.abc.com/static/css/style.css?v=v1.0.0。

这种方法的好处是实现简单,不用改文件名,适合小项目或者更新不频繁的场景。缺点是:有些CDN节点不会把查询参数当成不同的文件,可能还是会返回旧的缓存,需要在CDN控制台配置“忽略查询参数”为关闭状态,否则会有问题。

3.4 两种策略的对比

对比项 文件名加版本号 查询参数加版本号
实现难度 中等 简单
缓存可靠性 高(不同版本是不同文件) 中等(依赖CDN配置)
旧版本保留 支持 不支持
适合场景 大型项目、更新频繁、需要兼容旧版本 小型项目、更新不频繁

四、应用场景、优缺点与注意事项

4.1 应用场景

  1. 面向全国甚至全球用户的网站:比如电商网站、资讯网站,用CDN加速可以让不同地区的用户都有好的体验。
  2. 有大量静态资源的项目:比如带很多图片、视频的内容平台,CDN可以分担源站的带宽压力。
  3. 需要频繁更新静态资源的项目:比如迭代快的Web应用,版本刷新策略可以保证用户及时拿到最新的资源。

4.2 技术优缺点

优点

  1. 加载速度快:用户从最近的CDN节点拉取资源,减少网络延迟。
  2. 节省源站带宽:大量静态资源的请求由CDN处理,源站只需要处理动态请求。
  3. 可靠性高:CDN节点通常有容灾机制,某个节点出问题,会自动切换到其他节点。
  4. 版本刷新灵活:可以根据项目需求选择合适的版本策略,保证资源更新及时。

缺点

  1. 有一定的成本:CDN服务如果超出免费额度,需要付费,流量越大费用越高。
  2. 配置复杂:CDN的回源规则、缓存规则、版本刷新都需要正确配置,否则会出现问题。
  3. 调试麻烦:静态资源的更新可能需要一定的时间同步到所有CDN节点,调试的时候要注意缓存的影响。

4.3 注意事项

  1. 协议要统一:CDN域名的协议(http或https)要和源站一致,否则会出现跨域问题。如果源站用https,CDN也要配置https证书。
  2. 缓存时间设置:CDN的缓存时间不要设置得太短,否则会增加回源的次数,浪费带宽;也不要设置得太长,否则更新不及时。一般静态资源的缓存时间可以设置为7天,配合版本刷新策略使用。
  3. 避免不必要的缓存:动态生成的静态资源(比如用户上传的头像,每个用户都不一样)不要缓存,或者设置较短的缓存时间。
  4. 定期清理旧版本:如果用文件名加版本号的策略,旧版本的静态资源会一直存在CDN上,占用空间,要定期清理不需要的旧版本。

五、文章总结

给ThinkPHP的静态资源配置CDN加速和版本刷新,是优化网站性能的重要手段,核心思路是把静态资源放到离用户更近的节点,同时保证用户能及时拿到最新的资源。

整个流程其实不难,只要按照步骤配置:先准备CDN域名,然后改造ThinkPHP的静态资源输出方式,配置CDN的回源规则,最后选择合适的版本刷新策略。

对于个人开发者或者小项目,用查询参数加版本号的策略就足够了,实现简单;对于大型项目或者需要兼容旧版本的场景,文件名加版本号的策略更可靠。

最后要注意CDN的配置细节,比如协议统一、缓存时间设置、定期清理旧版本,避免出现不必要的问题。只要把这些步骤都做对,就能明显提升网站的加载速度和稳定性。