关于 Nodejs GLOBAL与多核心CLUSTER 问题的一些探究
关于 Nodejs GLOBAL与多核心CLUSTER 问题的一些探究
因为之前研究NODEJS自建缓存。但是在实际运营过程中发现问题。 因为跑缓存的变量是由GLOBAL定义的。
于是尝试了由GLOBAL -> cluster 的启动顺序。 在刷写缓存时失败。原因是 每个核心的GLOBAL 独立。要同步GLOBAL还需要使用process方法。
使用核心事件的监听的确可以同步GLOBAL做缓存。 但是很严重的一个问题,就是缓存副本过多。当作为生产环境时,吃内存现象极其严重。同步缓存时耗费系统资源过大。
最终还是放弃了CLUSTER+GLOBAL缓存的方式采用REDIS。
除了在服务器上另开一个单核实例作为缓存之外,有没有大神可以解决这个问题。
_(:з」∠)_研究中拜谢。
PS:在采用EXPRESS的过程中,发现MVC并不能高效发挥服务器性能。我个人采用了基于路由表的一种R2B(自称)形式。
关于 Node.js GLOBAL 与多核心 CLUSTER 问题的一些探究
在开发 Node.js 应用程序时,我们经常会遇到一些挑战,特别是在处理多核心和全局变量同步方面。本文将探讨使用 GLOBAL
变量和 cluster
模块时遇到的一些问题,并提供一些解决方案。
1. 使用 GLOBAL
定义变量
首先,让我们看看如何使用 GLOBAL
来定义变量。GLOBAL
对象是一个全局对象,可以用来定义在整个应用中都可用的变量。
// app.js
GLOBAL.myCache = {};
setInterval(() => {
GLOBAL.myCache[new Date().toISOString()] = Math.random();
}, 1000);
在这个例子中,myCache
是一个全局变量,可以在整个应用中访问。
2. 多核心和 cluster
模块
当你在服务器上运行多个核心时,cluster
模块可以帮助你创建子进程来利用多核 CPU。然而,每个子进程都有自己的 GLOBAL
上下文,这意味着它们之间的 GLOBAL
变量是独立的。
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
for (let i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
} else {
// 子进程
setInterval(() => {
GLOBAL.myCache[new Date().toISOString()] = Math.random();
}, 1000);
}
在这个例子中,每个子进程都会有一个独立的 GLOBAL.myCache
实例,导致缓存数据不一致。
3. 同步 GLOBAL
变量
为了同步 GLOBAL
变量,我们可以使用 process.on('message')
方法来监听消息并更新缓存。
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const workers = [];
for (let i = 0; i < os.cpus().length; i++) {
const worker = cluster.fork();
workers.push(worker);
}
setInterval(() => {
const data = { timestamp: new Date().toISOString(), value: Math.random() };
workers.forEach(worker => worker.send(data));
}, 1000);
} else {
process.on('message', data => {
GLOBAL.myCache[data.timestamp] = data.value;
});
}
然而,这种方法会导致大量的内存消耗和 CPU 开销,尤其是在高并发情况下。
4. 解决方案:使用外部存储
由于上述方法存在性能瓶颈,我们可以考虑使用外部存储,如 Redis,来管理缓存。
const redis = require('redis');
const client = redis.createClient();
setInterval(() => {
const key = new Date().toISOString();
const value = Math.random();
client.set(key, value, redis.print);
}, 1000);
通过使用 Redis,我们可以确保缓存数据的一致性和可靠性,同时减轻了服务器的压力。
5. MVC 和 R2B 架构
在使用 Express 进行开发时,我发现传统的 MVC 架构并不能充分发挥服务器的性能。因此,我采用了一种基于路由表的形式(R2B),以更好地利用路由和中间件的功能。
const express = require('express');
const app = express();
app.get('/data', (req, res) => {
res.json({ message: 'Hello, World!' });
});
app.listen(3000, () => console.log('Server started on port 3000'));
通过这种方式,我们可以更灵活地管理和扩展应用程序的路由逻辑。
总结来说,使用 GLOBAL
变量和 cluster
模块在多核心环境中可能会遇到一些挑战。通过使用外部存储和优化架构设计,我们可以更好地解决这些问题。希望这些示例代码能帮助你在实践中更好地理解和应用 Node.js。
用了 cluster 的话,就用到了多进程,每个 node 跑在不同的进程上面,如果要同步的话,一定是要另开一个进程来做通信的。
我觉得用 redis 的方案很好啊。
恩,我觉得redis很好了,不然你烧不慎,就可能出现内存泄漏
在Node.js中,GLOBAL
对象默认是在每个进程中独立的。当你使用cluster
模块来创建多个工作进程时,每个进程都有自己的GLOBAL
上下文,因此它们之间不会共享数据。这导致你在尝试通过GLOBAL
变量实现全局缓存时遇到问题。
如果你想要在多核环境下共享缓存,直接依赖GLOBAL
变量是不可行的。你需要使用一种跨进程通信或共享存储的方法。以下是一些解决方案:
-
使用Redis等外部存储: Redis是一个高效的键值存储数据库,可以被所有工作进程访问。这是你已经提到的方法,也是最简单和可靠的方法之一。
// 示例:使用ioredis库与Redis交互 const Redis = require('ioredis'); const redis = new Redis(); async function setCache(key, value) { await redis.set(key, value); } async function getCache(key) { return await redis.get(key); }
-
使用进程间通信(IPC): 你可以利用
cluster
模块提供的IPC机制,将缓存操作集中在一个主进程中,然后让子进程请求主进程来获取缓存值。// 主进程 const { Worker } = require('worker_threads'); process.on('message', async (msg) => { if (msg.type === 'get') { // 从缓存中获取数据 } else if (msg.type === 'set') { // 将数据设置到缓存中 } }); // 子进程 process.send({ type: 'get', key: 'example' });
-
使用内存共享库(如
shmdata
):shmdata
库允许你在一个共享内存段中存储数据,这样所有的工作进程都可以访问。// 示例:使用shmdata库 const shmdata = require('shmdata'); const cache = shmdata.create('cache', 1024 * 1024); // 创建一个大小为1MB的共享内存段 function setCache(key, value) { const offset = key.charCodeAt(0) % cache.size; cache.write(offset, value); } function getCache(key) { const offset = key.charCodeAt(0) % cache.size; return cache.read(offset); }
以上方法各有优缺点。使用Redis是最简单和最可靠的方案,特别是在生产环境中。而使用内存共享库或进程间通信可能需要更多的代码和调优,但可以减少对外部服务的依赖。