前言
这里的 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) => |
实现方式
客户端
客户端也能承担分流的任务,即像 cnpm 一样包装一层自己的 npm cli 工具,但分流的逻辑要简单许多,只需检查要安装的包是否属于私有,然后分为两批安装。
缺陷是推行难度和速度都不理想,于是这里只是顺便提一下。
服务端
到这一步,技术选型已经无所谓了,自然可以 nginx + lua,简单一点就继续使用 Node.js 实现。
由于其他原因,我用 express 做了实现,贴一点转发逻辑,大家就自由发挥吧。
1 | const request = require('request'); |
结果
在同样的测试条件下,私服的 /:package/:version? 接口平均响应耗时从 4s 降至 400 ms,可以明显感觉到速度的提升,并且可以通过不断扩展代理层优化处理效率。作为轻量级的私服解决方案,已经可以续命很久了。
后续
这个系列就此结束了吗?当然没有,cluster 的坑还没填呢!也确实可能会鸽掉…
因为支持 cluster 需要较深入的二次开发,也有新的中间件引入,相比目前的成本要高出不少。并且 Verdaccio 新旧版本的逻辑存在一定差异,我在老版本中已经解决了此问题,但新版可能又要另一套实现。
所以,等我读完 Verdaccio 最新的代码再说吧~