Verdaccio 性能优化:代理分流
前言
这里的 Verdaccio 是指用于搭建轻量级 npm 私有仓库的开源解决方案,以下简称 npm 私服。
前段时间写了一点分流相关的优化思路,但那是以节省资源开销为主、不冲破原有结构的微调,从结果上看,甚至不是合格的优化。
随着用户(请求)数量的上升,服务响应速度和效率其实才是最要紧的问题,节省资源终究不能改善这一点。因此我决定实施上次浮现在脑中的想法,将内外网的 npm 包流量彻底分流。
关于 Cluster 模式的说明
再次解释,Verdaccio 官方文档明确表示不能支持(PM2)Cluster 模式。另外,其云存储方案是可以支持多进程多节点部署的,但只提供了 google cloud、aws s3 storage 的插件。
不过在此基础上,只要拥有自己的云存储服务,就能使用或设计一套新的存储插件,进而支持多进程架构。此方案一定可行,只是相比本篇的做法,需要的成本更高一些。
俗话说得好,没有一个中间层解决不了的问题,而在 Verdaccio 的场景下,这种做法又是相当地迅速和高效。
原理
npm 安装机制
如果不了解 npm 官方客户端的安装机制,稍后可以阅读阮一峰的博客[[http://www.ruanyifeng.com/blog/2016/01/npm-install.html][《npm 模块安装机制简介》]],少部分知识已经不适用于当前版本了,不过最重要的是能理解 npm 下载流程。
其中我们需要知道,npm 包下载前,客户端会向上游服务器查询包信息,以及获取压缩包的下载地址 url,并将此 url 存放在 package-lock.json 文件中。以后每次执行下载,都会优先使用 package-lock.json 中的地址。
npm 下载最长请求路径
为了方便理解 Verdaccio 所处的位置,我来绘制一下 npm 包下载时从客户端到 Verdaccio 再到上游的最长请求路径简图,并忽略中间的安全验证环节,如下所示。

接口转发
有了代理层,就可以忽略 Verdaccio 内部的各种逻辑,不受技术栈的约束,编写少量的代码,便能完成主要接口的分流。
首要的接口是 /:package/:version? ,释放私服最大的查询压力,原因可以看这里的解释。
次要的接口是 /:package/-/:filename ,也就是实际的下载接口。并且其中还涉及另一个极为有利的优化。
尽管 Verdaccio 是转发上游的资源,它也会将下载 url 变更为自己的服务域名。因此不论依赖是否私有,记录到 package-lock.json 中的地址都是 Verdaccio 的地址。
但经过代理层的分流,此后经过更新的 package-lock.json 将保留原汁原味的下载地址,此后下载压缩包的请求再也不会发到私服。
综上所述,我们可以将私服超过 99.99% 的流量转移到代理或上游服务。
条件
接下来,我们来确定分流口径,自然是判断一个 package 是否是私服私有,因此需要 Verdaccio 提供接口,获取私有包的列表。
Verdaccio 有一个 /-/verdaccio/packages 接口用来获取所有私有包的信息,但这个包主要用于 Web 页面,包含大量我们不需要的信息,甚至简单一点,只要提供私有 npm 包的包名就能满足筛选条件。
因此,可以改良 /-/verdaccio/packages,例如新增一个专门获取包名列表的接口,并增加内存缓存。
Verdaccio 版本不同时,做法也有很大差异,相信这里的处理不是问题,只要认真阅读上述接口就能获取思路了。
PS:还是补充一点代码吧,早期版本 Verdaccio 只需要这样改:
1 | /** |
最新的 names 要使用回调的方式取值,伪代码:
1 | const names = await new Promise((resolve, reject) =>... |