主要分析下 src 文件夹下的代码。从简单到难吧:
utils.js
按照命名,肯定是封装一些常用的方法。这里只提供了一个 cpuNum 的 getter 接口。
这个是计算当前计算机的 cpu 核心数目,用于决定开启多大的集群进程。为了保证效率,最小的返回值是 4。
error.js
主要从 Error
对象上继承并且创建了新对象: VemoError
。对象构造函数上, 设计了一个有关此框架的错误状态码的信息 。
除了 VemoError
,还暴露了一些错误的代号定义。
cluster.js
对外暴露了自定义的高并发集群代码,它引用了同级的 ./process.js
封装的管理类,并且初始化了它。它的具体含义请见”process.js”中的讲解。
cloudbase.js
getLocalSecret
方法是读取本地用户的根目录中的 .tcbrc.json
配置文件,并且将 Id 和 Key 放入 process.env
变量中。
getTempSecret
方法是获取云主机的临时密钥:
- 正常情况下,访问远程地址,获取 id、key、token 和过期时间,并且放入
process.env
变量中。 有个不错的设计点:过期时间比远程动态减少 600s,主要是为了提前拉取 - 异常情况下, 如果没有到达最大失败次数,则在异常捕获中,调用自身,进行重试 。这里也是一个不错的设计点,包括参数的传递。(不需要用闭包)
- 改进意见 :
- 考虑到网络情况造成的异常情况处理,其实可以使用定时器调用。
- 检测位置可以提到函数入口,考虑参数为 (0, -1) 的情况
对外暴露:
async 函数,就是拿到有效的临时密钥,并且挂载上下文,继续执行后面的程序(next 参数)。
process.js
整体的设计思路是:一个主进程,多个工作进程。为了保证工作进程有效,又采用了”心跳机制” + “生命周期” + “定时检测”3 种机制来保证有效以及意外情况下的重启机制。
除此之外,在内存不足的时候,会自动降级服务!
内置方法
timer
函数,就是一个类似 setInterval 的函数。与 setInterval 不同的地方有 2 点:
- 回调函数的参数:当前的时间戳
- 第一次是立即执行的
restartWorker
函数,专门用来重启工作进程的函数。
它首先会检测距离上次批量重启的时间是否太短,太短,则跳过。 根据全局环境设置,这里的每次自动重启检测的时间是 60s。
否则,循环遍历所有子进程, 检查当前子进程的存活时间是否超过规定(60min) ,超过,就调用 killWorker 先新建再删除。
这个函数的精彩之处,在于时间上的处理。一言:每分钟自动检测进程更新,每个进程都会在 1 小时后被依次自动更新。
killWorker
函数,先启动新进程占位,再删除原进程。
- 对于新建的进程:监听 killSelf 新号,重建自己;监听 hearBeat 心跳信号。
- 对于被替换的老进程:先断开连接,再 2s 后 kill 掉。( problem:为什么先断链,再关闭呢 ?如果有大运算,调用 disconnect 会阻塞并不会关闭 IPC 管道。需要等待 2s 自定义缓冲时间, 再强制 kill ,不再调用 disconnect,因为还会阻塞)。
对外暴露方法
init
函数,初始化进程管理器:
- 对于主进程
- 根据 cpu 核心创建工作进程
- 每个工作进程监听 3 种信号并且响应
- 开启 restartWorker,对于超过生命周期的进程,自动重启
- 再定义一个 timer,检测每个工作进程存活状态,以及心跳是否正常
- 对于工作进程
- 加载 index.js 中的端口逻辑,若有出错,则告知主进程,并且关闭主进程( problem:主进程会自动关闭其他工作进程吗? cluster 已经帮忙做了,linux 下通过 ps -aux | grep “…” 可以查看)
- 定义一个 timer,向主进程发送心跳包
- 定义一个 timer,检测内存占用过高,主进程关闭工作进程( problem:process.memoryUsage()是全部的吗?是当前进程的信息 )
reload
函数,重启全部进程:
这个设计的很巧妙,因为全部工作进程的创建时间都放在 workerCreateTime
对象中,主进程中又开启了自动重启子进程的 timer(在 120 行)。所以这里直接一个循环,将其所有属性置 0。
当然,这并不是同时重启,每个子进程的重启有个间隔,这个间隔可以改进,因为这个间隔期间就是服务器响应能力比较弱的时候
index.js
在工作进程启动端口相关的服务,主要分为 3 个部分:普通 http 服务、websocket 服务以及静态服务器服务。
请分别调研使用它们的服务对应的库的用法。