短视频网站优化及 Cloudflare 初体验笔记
1. 首页加载速度优化。
弃用 https://github.com/naver/egjs-infinitegrid,改用 css 原生的 columns
实现瀑布流。
这个 infinitegrid 乍一用还挺不错的,瀑布流布局、无限加载,完全符合我的需求。但是它的默认行为是阻塞式的,即在所有项(我的是图片)加载完成后再一次性渲染。虽然其文档上有提到异步加载方式,我简单尝试了下,没成功。
columns
默认会打断(break)元素。比如我的元素是这样的:
1 | <div class="container"> |
top 和 bottom 有可能就会被换行处理。可以通过设置 container 的 break 相关属性改变行为[1]:
1 | .container { |
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 之后也依然支持边下边播。
参考
- 1.How to prevent column break within an element?: https://stackoverflow.com/questions/7785374/how-to-prevent-column-break-within-an-element ↩
- 2.理解 MP4 moov atom: https://ermao.live/理解MP4-moov-atom.html ↩
- 3.Default Cache Behavior: https://developers.cloudflare.com/cache/about/default-cache-behavior/ ↩
- 4.Caching levels: https://developers.cloudflare.com/cache/how-to/set-caching-levels ↩
- 5.Best Practice: Caching Everything While Ignoring Query Strings: https://support.cloudflare.com/hc/en-us/articles/360023040812-Best-Practice-Caching-Everything-While-Ignoring-Query-Strings ↩
- 6.React + Videojs + Sanic 打造简易流播放器: https://ermao.live/React+Videojs+Sanic打造简易流播放器.html ↩