Skip to content

Engine

ClientWorker中拥有五款原理不同的并发引擎,他们分别是fetch Crazy Classic Parallel KFCThursdayVW50

虚假的疯狂引擎 Crazy 真实的疯狂引擎KFCThursdayVW50

KFCThursdayVW50原名Hysteria(歇斯底里的),但再疯狂也比不过周四KFC吧:)

在这其中,fetch(JS原始请求方式) 与 Crazy是单请求输入引擎,ClassicParallelKFCThursdayVW50 是多请求输入引擎。这意味着后面两个引擎可以同时对多个地址发起请求,而前面两个引擎只能对单个地址发起请求。请注意,除了fetch 之外,其他引擎都是多线程的,非多线程请求将会被降级。

注意:fetch Crazy 只接受字符串形式的url Classic Parallel KFCThursdayVW50 只接受数组形式的urls

Promise.any的兼容性远低于ServiceWorker,ClientWorker会自动对其PolyFill,因此ClientWorker兼容性最低要求与ServiceWorker相同。

在刚开始作者曾尝试用SetTimeout + Promise的方案尝试前端并发,但最终还是发现Promise.any是前端最快的并发方案,没有之一。

Classic

由于js界对于ServiceWorker热度属实不高,对于ServiceWorker的使用也大多局限于PWA、缓存和加速,有关其研究和开源项目属实不多。为数不多的几个热门项目jsproxyfreecdn,两者都是用ServiceWorker开发,但是网络上大部分人只关注效果,很少有人关注原理,对其并发引擎研究也甚少。

作者在先前研究过freecdn的并发原理 (但由于作者太菜了没看懂) ,于是作者自己琢磨了一套方案,并在2022/1/6 第一次提交到了自己的博客,效果出奇的不错。

原理详见作者博客 - 欲善其事,必利其器。核心无非是利用重构Request阻塞fetch,避免被提前Abort

后来我重新看了一遍freecdn的url-loader才意识到原来EtherDream用的也是这一套方案。我瞬间觉得我厉害了起来

自然,Classic是无法处理流下载和直播(指flv阻塞性直播,对于切片直播无影响)的,并且对于较大的图片也无法做到边下载边显示。如果你比较反感这个,可以考虑Parallel

Parallel

然而Classic引擎既然是重构Request,那不可避免的会造成性能折损。在并发10MB的请求下,单线程的下载时间是670ms,而多线程一度达到了850ms,延长了将近三成时间。

此外,由于通过阻塞方式绕过状态检测会不可避免地造成冗余流量,上述实验中原图片10.3MB,实际流量13.7MB,虽然不是特别大但这一情况还是存在。

感谢@186526,是他提醒我试试Event调度,并且给出了样例代码,在此基础上我才得以开发出第二套引擎Parallel这一引擎。

Parallel在并发时会给予每一个进程标记Event,并在获取到正确的Status后广播打断消息而避免自己被打断,将整个Instance(而非Response)返回,实际上Parallel才更接近正常人所认为的并发引擎。

Parallel在理论上由于是直接转发结构没有性能折损;至于流量损耗,请求获得Status所消耗的流量相对于内容主体来说几乎忽略不计。

Parallel弊端是如果多个并发中,有一个很快地响应了状态代码,但是下载速度很慢,这也会严重拖慢速度。

在2.1.0及以上版本,ClientWorker修复了Parallel对相对路径错误处理,采用重构Request的方式但保留body,保持其性能不折损。

Parallel支持流下载.

Crazy

Crazy会在刚开始发起一个只下载1个字节的请求Range 0~1,用于检查文件总大小(这被称为PreFetch)。如果总大小不存在或者比线程还小,则会降级为normal fetch

此后Crazy将发起共线程数个大小为总大小÷线程数的请求,并发下载,最后合并为一个响应主体,并将PreFetch中的标头与状态还原。

由于浏览器对于一个域名的请求限制,我们通常建议将线程数调小为4以下

这种方式推荐请求字体、无法流式的图片、无法流式的音视频等等。

对于较小的文件,加速效果并不显著,乃至可能会出现减速

(4线程)通常10M左右的字体可以显著提速1.5倍上下

这是一张近7M的图片,你可以打开F12观察它的请求状况

KFCThursdayVW50

一个疯狂的引擎...

KFCThursdayVW50会在刚开始对所有源发起一个只下载1个字节的请求Range 0~1,用于检查文件总大小(这被称为PreFetch)。如果总大小不存在或者比线程还小,则会降级为parallel

此后KFCThursdayVW50对每一个源发起共线程数个大小为总大小÷线程数的请求,并发下载,在任何一个源响应正确代码后打断当前组的其余请求,最后合并为一个响应主体,并将PreFetch中的标头与状态还原。

并发的总请求为线程数*源个数

这种方式可以叠加不同镜像的带宽,适合下载较大的文件。

非常不建议对静态资源使用此引擎,过多的线程可能会导致用户浏览器意外崩溃。

对于较小的文件会起到减速的效果

(4线程,4镜像)对于一个存储在Sharepoint的2GB文件,可以显著提速近7倍速度(30MB/s->200MB/s)