之前我在 Hexo + Mathjax: 公式离线渲染 介绍了在服务器端,也就是 Hexo 端渲染时就将 Mathjax 公式渲染成 Html 的方法。不过随着文章数量的增加,Hexo 渲染时会出现并发渲染公式的页面太多导致服务器内存溢出的问题(我用一个小虚拟机来做服务器博客后端,内存只有 2 GB)。因此有必要控制公式渲染的并发数。另一方面,公式渲染时纯 CPU 计算过程,不涉及 IO,因此使用异步函数加速意义也不太大。因此我们的核心思想是把之前实现的异步渲染版本换成同步渲染。

为了更具一般性,我们实现了一个能够控制并发渲染的页面数量。当并发数为 1 时,渲染就成为纯同步的。

这里我们使用了 asyncLimit 库。这个库提供了一个函数『装饰器』,可以将任意异步函数转化称可以限制并发数的版本。我们来看下面的例子:

1
2
3
4
5
6
const readFile = require('util').promisify(require('fs').readFile);
const asyncLimit = requrie('@ycm.jason/async-limit');

const limitedReadFile = asyncLimit(readFile, 10);

// limitedReadFile can only have 10 concurrent calls at the same time

这里 readFile 这个异步函数在经过 asyncLimit 的处理后得到的 limitedReadFile 函数能够并发调用的并发数不能超过 10。

在这个库的帮助下,我们不需要修改之实现的 math.js 中的内容,只需要在注册相应的 filter 的时候将相应的过滤器执行函数交由 asyncLimit 处理一下,即下面这个样子:

1
2
3
4
5
6
7
const asyncLimit = require("./lib/asyncLimit");

// 公式后端渲染
hexo.extend.filter.register(
"after_post_render",
asyncLimit(require("./lib/math"), 1)
);

这里并发数限制的是 1,这会使得渲染变成同步的。

最后提一下几个读者可能会遇到的问题:

  • 当并发数大于 1 时,可能会出现渲染报 Timeout 错误。

    这个问题我也没有找到原因和合适的解决方法,手动去设置 timeout 相关的设置也不行。最好的解决办法是将并发数设置成 1。

  • 并发数设置成 1 是否会影响多核 CPU 的渲染进程?

    这个应该不会。因为我们限制的同步渲染实在同一个进程内部,而 Hexo 在渲染时会根据 CPU 的核心数创建不同数量的子进程,同步渲染是在同一个进程内部的,不同子进程之间仍然是并发的。