cloudflare缓存与绕过缓存

上一篇 Koikatsu CharaStudio MMD浅入浅出 的衍生文章。在这之前,cloudflare那边的配置我基本就没怎么动过,一直开cache everything的策略来降低回源数据的……

先说现象

上篇文章贴了个两视频:一个100多M的av1 60fps格式的视频,90来秒;还有一个是600多M的h264 120fps格式的视频,3分半多一点。并且两个视频的预加载都是metadata级别的,代码贴下面:

1
2
3
<video controls preload="metadata">
<source src="https://cdn.zhouxuebin.club/data/2024/11/uchoten_bibache_h264_120fps.mp4" type="video/mp4">
</video>

本地测的时候没啥问题,两个视频都能正常加载播放。后面把url改成cdn之后,100多M的视频能够正常加载。但600多M那个视频,预载metadata的时候直接把整个文件几百M下了一遍,然后播放的时候又把那几百M重新下一遍,一趟折腾下来,就1G多的数据量了,明显不对劲,而且也不是必现的,偶尔也只需要正常加载个1M不到的数据就好了。

开始分析背后的成因

chrome的video tag都是通过流式传输按需加载内容的,背后使用HTTP的Range header来指定从哪里下到哪里。

正常来说,web网关在收到Range请求后,理论上返的HTTP状态码应该是206 Partial Content:

但现实是什么个情况呢?返了个200……而且响应头里面关于请求Range部分的数据都被丢弃了

一时排查不清是chrome的问题,还是cloudflare的问题,还是web网关的问题 (当然现在都是后日谈了) 。开始用控制变量法排除问题。

第一步,把cdn的url改为不经cdn的直连url,在dev tools把disable cache勾上,刷了十几遍,没有复现200的情况,基本可以排除chrome这选项了。

第二步,把web网关的log level改成trace,直接把所有请求和响应的header都log出来。直连情况验证没问题,这次直接用cdn的url发get请求。然后,神奇的事情发生了,web网关的请求header里面没有Range字段,然后很自然返了个200回去……似乎web网关也没有做错什么事情。

那么,就剩下cloudflare了……搜了一下其他人用cloudflare中转视频数据时也会遇到这种情况来看,大概能猜到个大概缘由了。它到底干了什么?其实,它也只是在缓存数据罢了。 毕竟开了cache everything嘛。

不需要串流,且文件大小也不大的文件,cloudflare会直接回源拉一次数据,后面就直接从cloudflare缓存里面拉了。

需要串流,且文件大小也不大的文件,cloudflare似乎也只会回源拉一次数据,而回源拉的数据,是不带Range header的。后续的Range请求,cloudflare能够命中缓存,而且缓存也能够正常处理Range请求,所以没有暴露出问题。

需要串流,文件大小超过cache大小的文件,问题就暴露出来了,cloudflare回源拉的那次数据没有Range header,而且Content-Length超过cache上限……表现是这样的:浏览器发了一条range请求,cloudflare的cache机制把range丢掉,给web网关发了条回源的get请求,web网关就这样老实巴交地给了个200的状态码回去了。cloudflare提供的header里,可以清楚看到cf-cache-status状态是miss,content-length就是整个文件的大小……这就是所谓的聪明反被聪明误吧……

最后给个解决方案

核心思路其实很简单,能cache就cache,cache不了的串流文件,那就直接绕过cache嘛。谷歌一下,就有一堆的方法,但比较简单粗暴,不尽完善。比较常见的就是:关掉全局cache,又或者是绕过某类mime-type的缓存。但怎么说呢,这种规则能够处理场景似乎比较死板……思来想去,要不就在url query string上下点功夫得了。

我个人的方案其实也很简单,两条规则足矣:全局cache保持打开状态,绕过某些query string的url。这样配置cache影响范围比较小,用途也算比较灵活。

第一条是古早的全局cache:

第二条是新增的绕过cache的规则:

用途也很简单,在需要绕过cache的url,后面统一加上?_cf_bypass_cache=1就行,原本有query string的也能用

还是用上面的url测试,可以看到cf-cache-status现在固定是dynamic了,对应的Range请求也没丢掉

大功告成(鼓掌鼓掌),可以丝滑加载了。

后面有个想法把data里面的数据迁到云存储上,走动态fetch,至于会不会这么做,以后再说

时隔一年的回归

研究生毕业之后,好像这里就已经被荒废掉了。其实之前想过好几次想要复更点啥的,但是一直拖着拖着就没了下文了。今年年初换完电脑之后升了win11之后就更没有想法了,一点进文件夹就想到还得装一堆环境,好烦……但无论怎么说,耗了好几个月终究还是把环境给整全了。

电脑是去年双11买的,因为拖延症加上以前的8700k+1080ti的电脑还能用,所以就一直拖着没装。但不管怎样,年初花了一个月时间熟悉新系统,还算是挺香的,微软最伟大的贡献就是能在wsl2的linux系统跑GPU了。

毕业之后,没继续走AI这方向就业,跑去当后台开发去了……

怎么说呢,AI这东西我个人觉得投资回报的风险太不可控了,就跟赌博一样……赌赢直接退休,赌输了那研发成本就白白烧掉了。之前在实习的时候,做的就是科研落地方向的,最后的产出指标量化下来就是GMV这类的指标了。如果运气非一点,最后模型上线做完AB测下来,发现GMV没提升多少,或者跟离线的指标差一点,组会做汇报的时候就显得稍微有些尴尬了。

当然这也是少数情况啦,大牛永远都会是大牛。之前同一部门的实习伙伴们,有发了不少paper,有跑去MIT留学,然后现在在折腾初创公司的……比如RMVPE: A Robust Model for Vocal Pitch Estimation in Polyphonic Music这篇文章的一作魏佬,当时看到他发朋友圈,把我震惊到了。

虽然自己工作跟AI无关了,但是科研领域的动态还是会关注的。套用三体的话来说,我是坚定的降临派分子。遇到自己感兴趣的新玩意儿,还是会花点时间看一下的。

经过研究生三年的经历下来,我发现自己真正喜欢的只是他人的研究成果,而不是享受研究这个过程。纯个人感觉,这过程其实跟坐牢差不多吧。当年的生活,无非是每天早上check一遍跑了一晚的实验数据,看完数据没问题,换个参数继续跑,有问题还得去看代码哪里算错了……等到结果差不多全了,就得好好整理实验数据写manuscript了,写完,投,accept with revision还好,rejected的论文还得再找个合适的期刊,换个latex模板,重新投……而且每次revision都得回复reviewer的各种奇奇怪怪的意见,比较无语的就是lack of innovation这种广大空的话了……后面有一篇论文revision的时间跟毕业论文初稿重叠了,是去年年初的事了,那段时间过得可酸爽,早上起床改&写论文一直改到深夜……

咳咳,扯远了。后面嘛,预期会更点目前在玩的东东(准确来说是前几个月了):SO-VITS-SVC,整个AI翻唱的流水线&原理,先留个坑在这里。这里先发个成果,fgo妖兰(cv 高野麻里佳)翻唱:

模型是年初换电脑后当新显卡的benchmark跑的,不得不说,跟之前的1080ti比起来,4090确实是快啊……PS: 跑分软件也就图一乐,真要体验实际效果还得靠pytorch。

SO-VITS-SVC AI翻唱:坂本真绫

b站天天给我推AI翻唱,确实挺有意思的,趁着这几天有空跑一个折腾折腾。

Data preparation#

数据集是废狗的解包语音,文本爬的fgo wiki。原本是打算跑VITS的fine tuning做文本转语音的,但好像练出来的效果不太行就放弃了。SO-VITS-SVC做声音转换反而不需要用到标注数据。

去掉多CV的角色,筛选2-30秒内的音频,得到的时长分布如下(右键在新标签页上打开图片可以看到无缩放版本的图片):

坂本阿姨配的角色还挺多(贞德、达芬奇&若干马甲),1个半小时的语音时长应该够训练了。切片之后得到将近1000条3-10秒内的语音。

Training#

训练必须加上底模(D_0.pthG_0.pth)。小数据集只能做fine tuning,不足够从scratch开始训练的。自己试过从头开始跑,跑了14w+ steps还不如加了底模跑个1w steps的效果。

batch size设为8,跑了64k steps,tensorboard的log长这样:

Ground truth sample:

Generated sample:

生成的声音还是有点沙哑,等以后补充数据集后力大砖飞跑个几天试试。

SVC Inference#

翻唱的话得分几个步骤:

  1. 用UVR5分离人声和背景音
  2. 将人声音频放到so-vits-svc项目路径的raw文件夹里,跑so-vits-svc的inference_main.py,指定一些推理参数,比如:-m logs/44k/G_64000.pth -c configs/config_768l12.json -t 0 -s maaya_preproc -cm logs/44k/kmeans_10000.pt -cr 0.2 -sd -15 -fmp(聚类模型权重-cr看模型效果,太大了确实容易口胡,-fmp确实能够解决高音变哑的问题),转换后的音频放在results文件夹里头
  3. 将背景音和转换后的音频合并起来就ok了

UVR5不支持CLI,就差它实现一键转换大礼包了。

随便跑了10来首歌试了下训练效果,发现还挺容易口胡的,遇到和声和高低音直接开摆。
(版权声明:以下歌曲仅作科研使用,版权归网易云所有,请自觉遵守相关法规)

第一首:supercell - 君の知らない物語

第二首:森永真由美 - Opposite World

第三首:幽閉サテライト - 月に叢雲華に風

最后一首:坂本真綾 - さいごの果実

听得噪中噪,方为人上人(手动doge脸)

本地部署NovelAI:生成小黑贞

自从部署了Novel AI之后,连steam里面的游戏都没打开过了,这才叫真正的3A大作啊。

说实话以前一直想爬danbooru的图(主要是为了拿到tag),但是由于各种事情最后还是咕咕咕了。pixiv上的我倒已经爬了1个多T的日榜原图了,要不下次就一步到位爬熊猫站吧(手动狗头)。

戳这里看本地部署教程,不重复造轮子了。这里只说说自己改动的一些地方,和踩过的坑:

  1. 自己的显卡(1080 Ti)只能带得动animefull-final-pruned,并且会用掉将近10G的显存。animefull-latest大概会占用14-16G,具体哪个数值忘了,反正3090轻松秒杀。
  2. 上面的部署教程提供的sentence-transformers_all-MiniLM-L6-v2里面的tokenizer.json是有问题的,自己去hugging face下一个替换掉就好了。
  3. Novel AI的前端API有2分钟的等待时间,超时就会中断请求。改static/_next/static/chunks/pages/_app-xxx.js这个文件,Ctrl+F查找替换,把12e4(120000毫秒,即2分钟)改成36e5(1小时)就行,改长一点也没关系。
  4. Novel AI和stable diffusion的webui对prompt处理的区别:
    1. Novel AI用{}强调关键字,一个大括号权重乘1.05;stable diffusion用()强调关键字,一个小括号权重乘1.1。施法还是要注意一下这点区别的。
    2. stable diffusion支持(word:weight)直接给word更改权重,比如(1girl:1.2),但Novel AI没见到有这功能。
      为了统一一下咒语的效果,这里魔改的地方如下:

      上面的部分是把()加入到强调字符里,并且把权重改为1.1。改动的地方见行号右边标蓝的部分(比如157、165-166、171-178行)。
      下面的则是支持(word:weight)语法的(其实{word:weight}也行):
    3. Novel AI默认开启了Add Quality Tags,会在prompt前面增加masterpiece, high quality这两个prompt,negative prompt在Undesired Content这里,用它内置的其实也差不多,咏唱别人的咒语的时候需要改成None,然后把咒语粘贴到下面的框框里。

安装也不算复杂,对我来说与其折腾venv还不如直接新建一个conda环境……

1
2
3
4
5
6
7
conda create -n novelai python=3.8
conda activate novelai
conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch
conda install dotmap fastapi omegaconf einops torchdiffeq jsonmerge -c conda-forge
conda install uvicorn transformers scikit-learn ftfy scikit-image
conda install faiss-cpu -c pytorch
pip install sentence_transformers pytorch_lightning

下面就从最基础的咏唱开始展示,话不多说。

混进了一只巴御前

别的不说,Novel AI这黑贞画的已经比我强了(怒摔数位板)。