短视频网站优化及 Cloudflare 初体验笔记

1. 首页加载速度优化。

弃用 https://github.com/naver/egjs-infinitegrid,改用 css 原生的 columns 实现瀑布流。

这个 infinitegrid 乍一用还挺不错的,瀑布流布局、无限加载,完全符合我的需求。但是它的默认行为是阻塞式的,即在所有项(我的是图片)加载完成后再一次性渲染。虽然其文档上有提到异步加载方式,我简单尝试了下,没成功。

columns 默认会打断(break)元素。比如我的元素是这样的:

1
2
3
4
<div class="container">
<div class="top"></div>
<div class="bottom"></div>
</div>

top 和 bottom 有可能就会被换行处理。可以通过设置 container 的 break 相关属性改变行为[1]

1
2
3
4
5
6
.container {
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid-column;
display: table;
}

columns 还有一个比较无解的问题是不能无限加载,如果强行动态地添加元素进去,实际会引起整个页面重新布局,很不友好。

2. 只发布 MP4 格式视频

2.1 弃用 webm,系统/浏览器兼容性不如 MP4,小破站也没有那么多空间存储两种格式,索性就发布前全转换成 MP4。

2.2 使 MP4 支持边下边播[2]。简单来说就是在 MP4 文件头处加一些 meta 块,这样只需要读取少量文件片段后就大概知道 MP4 的整体信息,可以开始播放而不必等到读完文件了。

2.3 缩小分辨率高于 1080P 的视频。从 P 站搞来的一些资源都是 2K、4K 的,完全没必要。我的小破站大部分逻辑都是模仿 instagram 的,播放窗口最大才 888px(当然全屏就不是了),大部分情况下超过 1080P 的简直就是浪费。

这部分看着麻烦而已,其实都是 ffmpeg 的初级应用场景—— FFMPEG 万岁!

3. 上 Cloudflare!

如果站点是面向国内的,就别硬上 CF 了,真的是负优化。

CF 能干啥呢?

  • 反向代理,隐藏你的服务器 IP
  • 抗揍(DDOS),抗爬(Crawler)
  • CDN 缓存加速

前两条,基本不需要额外做什么,我也不太关心。关键是 CDN 这个,太有吸引力又太有挑战性了。

3.1 CF 默认缓存行为[3]

符合两条 CF 才会默认缓存你的资源:

3.1.1 response 带上 Cache-Control 头,且其值应是 public 和大于 0 的 max-age

我把返回缩略图和原始视频、动图的 API 都加上了一年的缓存:

1
'Cache-Control': 'public, max-age=31536000'

3.1.2 扩展名在 CF 的支持列表里。

Cloudflare only caches based on file extension and not by MIME type.

这句话很关键,关系着 API 设计问题。

比如我有一个帖子,对应的视频路径是 xxx.mp4,当我设计点赞、浏览记录类似的 API 时,很自然地就写成 /api/posts/like/xxx.mp4/api/posts/view/xxx.mp4 这样了。

如果引入了 CF,CF 看到 URL 带有 mp4 这样的扩展名,会错误地把这些 API 的回复也缓存了——这肯定不是我想要的结果,这些 API 的回复都是动态的内容啊。

反之,对于你真得想缓存的 jpg 啊、mp4 啊,你的 URL 就必须得有对应的扩展名才行了。

我采取了一种偷懒的做法:对于不需要缓存的 API,把其中的路径用 base64 编码了一下。

3.2 Page Rules

CF 全局的缓存策略(Caching Level)有三个选项值[4]

  • No Query String:只有在没有查询字符串时才缓存
  • Ignore Query String:忽略查询字符串,即不同的查询字符串也返回之前的缓存
  • Standard (Default):查询字符串不同即认为是一个不同的资源

我的主要缓存内容是图片和视频,理论上直接用 Ignore Query String 设置这个全局选项就可以了。但是太过霸道,万一哪天我利用了查询字符串然后又忘记改这里,估计查问题得挠破头。

合理的做法是利用 Page Rules 对不同资源分别配置策略。

这里多啰嗦一句 Page Rules 里出现的 Cache Everything 这个选项。发现网上很多人对它的理解是错的,动辄就用这个选项来兜底。它跟 Ignore Query String 不具有包含关系[5],即它不具有后者的能力。它更像是 Cache Every Extension + Standard 的组合——它强调的是忽略默认扩展名缓存任何 * 这个通配符匹配到的资源。

3.3 去掉文件流类型(file_stream())的 response

在之前,我为了能让视频边下边播,使用到了文件流的 response[6]。即返回 206 状态码。

但这种做法不适用于 CF CDN,会触发 HeaderNotFound("Range Header Not Found") 这样一个 Sanic 异常。

说明 CF 在转发请求时没带 Range 头?搞不懂原理,但解决办法是有的:直接返回文件 response 即可(考验源站到 CDN 节点的连通能力)。实测证明 CF 在缓存了 mp4 之后也依然支持边下边播。

参考

评论