Node.js Express服务器:使用Node.js线程池运行res.render() / ejs.render()

3
我们有一个应用程序,使用EJS模板进行服务器端渲染,以实现SEO目的。
我熟悉Node.js,并知道可能可以利用Node.js线程池进行异步I/O,无论是好还是坏的想法。目前我正在思考是否可能在线程池中的线程上运行ejs.render()或res.render(),而不是在Node.js的主线程中运行?
我们在渲染函数中进行了大量的重型计算工作,我们肯定希望将其从主线程中移开,否则我们将为更多的服务器支付$$$。

我们可以使用单独的Node.js进程来运行渲染,甚至可以使用webworker-thread(https://www.npmjs.com/package/webworker-threads),但我特别想知道是否有一种方法可以利用V8中现有的线程池。 - Alexander Mills
3个回答

1
EJS被认为是同步的,这一点不会改变,因此它是Node.js中一个低效的渲染引擎,每当渲染视图时都会阻塞JS线程,这降低了您的总吞吐量,特别是如果您的渲染需要大量CPU计算。你应该考虑其他选项。例如:https://github.com/ericf/express-handlebars 如果您的Web服务器确实具有CPU密集型计算,则Node.js绝对不是正确的工具。有更好的服务器来处理多线程和并行处理。您可以将Node设置为控制器,并将CPU密集型请求转发到可以完成繁重工作的后端服务/服务器。
看到您在渲染期间进行了什么样的计算将有助于提供更好的答案。
利用由libuv处理的线程池可能是个坏主意,但当然是可行的。您只需要一些C++技能和libuv库的uv_queue_work()方法来安排在工作线程上的任务。

你知道哪些适用于Node.js的异步模板引擎? - Alexander Mills
使用单独的node.js进程进行渲染不会太困难,但我想这个问题最初是在尝试寻找有关如何从主线程内部访问node.js线程池的信息,如果可能的话。对吗?因为你引用的这些异步模板库必须正在执行此操作,除非它们只是以某种方式使用流,并且不在主线程上执行工作。 - Alexander Mills

1
我尝试构建一个在分叉进程中运行的脚本引擎(阅读node的子进程模块这里)。我发现这是实现渲染引擎的有吸引力的方案。是的,存在传递参数(post/get查询字符串、会话状态等)的问题,但它们很容易处理,特别是如果您使用分叉选项(而不是exec或spawn)。有标准的消息传递方法来在子进程和父进程之间进行通信。
唯一的开销是生成额外的node实例(渲染引擎本身)。如果您在脚本引擎中进行了大量计算,则每个渲染请求都需要分叉一个新进程的一次性开销将与渲染所需的时间相比微不足道。
如果EJS渲染阻塞了主线程,则仅此一点就足以不使用它,特别是如果您在渲染过程中进行任何重要的计算。

1

你是否只是担心渲染问题?还有其他模板引擎可以产生更好的结果;因为模板渲染应该是幂等操作,所以你可以在集群中进行分布式处理。

V8会将你的代码编译成汇编语言,如果你没有遇到任何非优化或垃圾收集器阻塞的情况,我认为你应该接近网络I/O限制。我强烈建议您尝试其他模板引擎,在前端添加缓存HTTP反向代理并运行一些基准测试。


很遗憾,目前我们没有重写使用不同的模板库的方法,但是渲染让我担忧,它需要大量的 CPU。 - Alexander Mills
@AlexMills 我仍然会尝试添加缓存反向代理并运行一些基准测试,即同时随机打开robots.txt中的所有文档并测量负载。你唯一可以倾向于的下一步是找到一个本地模板引擎 - 我不知道这会提高多少性能,因为字符串操作的测量单位是每秒数百万次;它还将强制您重写模板代码,因为EJS可以包含任何JS代码的评估,而在Node.js之外实现的模板引擎肯定不会。 - Filip Dupanović

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接