在生产环境中使用MemoryStore

126

今天我第一次以“生产”模式运行我的Node.js应用程序,收到了以下警告:

Warning: connection.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and obviously only work within a single process.

我只需要运行一个单独的进程,但应该使用什么替代品呢?我希望我的会话驻留在RAM中以便快速访问。我还希望能够通过简单关闭Node应用程序来丢弃所有会话。

安装Redis、MongoDB或其他数据库似乎对于这个简单的任务来说过于冗余了。我也不理解为什么在Node中包含了MemoryStore,实际上它并不应该被使用?

9个回答

82

经过与 Connect 开发人员的交谈,我得到了更多信息。这里有两个被认为是内存泄漏的问题:

  1. JSON 解析问题,在最近版本中已经修复
  2. 如果用户从不访问过期会话,则没有清理它们的机制(即仅在访问时进行清理)

解决方案似乎相当简单,至少这是我计划要做的:使用 setInterval 定期清理过期的会话。MemoryStore 提供 all() 函数来获取列表,并可以使用 get() 函数强制读取并使其过期。伪代码如下:

function sessionCleanup() {
    sessionStore.all(function(err, sessions) {
        for (var i = 0; i < sessions.length; i++) {
            sessionStore.get(sessions[i], function() {} );
        }
    });
}

现在只需要通过 setInterval() 定期调用 sessionCleanup,就可以自动清理过期的会话,避免内存泄漏。


3
我发现你也可以使用Redis和Mongo作为后备存储,并让数据库自动清理。在Mongo的情况下,你可以设置过期日期并确保索引。 - huggie
62
在那里放置这条信息真是太愚蠢了。就像在说:“我们本来可以做得对,但我们偏要把它搞砸,然后让你们在互联网上玩猜谜游戏。哈哈!失败者!”请注意,翻译尽力保留原文的语气和情感,以便更好地传达原意。 - catamphetamine
2
变量 sessionStore 的值是多少? - Dimitri Kopriwa
4
sessionStore?这是一个Node.js全局变量吗?如果不是,我该如何在Node.js中获取对session Store的引用? - windchime
1
@MilanBabuskov - 你把这个放在你的app.js文件里吗?在express session中是否有发出回调的选项?更详细的实现方法会更好。 - Jonathan Bechtel
显示剩余2条评论

49

因此,对于这个问题被接受的答案 [编辑:曾经] 几乎是一种 hack,而其他答案只是建议使用数据库,我认为这是过度设计了。

我也遇到了同样的问题,只需用 cookie-session 替换 express-session 即可。

要做到这一点,只需安装 cookie-session

npm install cookie-session

然后在你的app.js文件中,找到使用express-session的地方并将其替换为cookie-session

app.use(require('cookie-session')({
    // Cookie config, take a look at the docs...
}));

您可能需要更改其他一些东西,对我来说只是一个简单的替换,没有任何伤害。


2
你是怎么做到的。 - Sid
2
这实际上是一个有用的答案。谢谢。 - smonff
9
cookie-sessionexpress-session 的区别在于,前者将数据存储在客户端,而后者将数据存储在服务器端。 - George

30

MemoryStore仅适用于(快速)开发模式,因为如果您的应用程序重新启动(进程终止),则会丢失所有会话数据(该数据存储在该进程的内存中)。

如果您不想使用数据库,请改用加密cookie存储。

http://www.senchalabs.org/connect/cookieSession.html


12

还没有尝试过,但看起来这是一个很好的解决方案(如果 OP 可以接受应用程序重新启动时丢失会话)。 - stujo
1
从您的链接中:“强烈不建议在生产环境中使用它!”问题是关于“在生产环境中使用MemoryStore”。 - Damien
1
没错,只是因为“你不能与其他进程或服务共享会话。”这意味着,还有其他更实用的解决方案。但这确实解决了内存泄漏问题,所以如果这些不是您关心的问题,那么使用它是可以的,并且解决了问题的关注点。 - Dovev Hefetz

8
我认为网络上的共识是使用数据库,但如果您肯定不想这样做,那么可以忽略警告——警告不是法律规定。
然而,既然您和我都认为内存泄漏是一个真正的问题,那么很难证明redis过剩了,因为它可以解决您的问题。
引用:“我也不明白为什么MemoryStore被包含在Node中,因为它并不真正被使用。”
这是一个很好的观点,但我想说的是,Node本身直到最近才真正成为“生产就绪”的状态。有些人可能不同意这种看法。

2
抑制警告并不能消除内存泄漏。 - Milan Babuškov
这就是为什么他们建议不要在生产中使用它的原因。那么,替代方法就是使用另一种存储方式,对吧? - Kristian
1
我想我只是不喜欢到目前为止找到的替代方案。我正在寻找一个简单的内存中JavaScript替代方案,而不是数据库系统。我真的不明白为什么他们不修复它。我的意思是,一个简单的Memory Store有多难...我只是希望有人已经做过了,这样我就不必重新发明轮子了。 - Milan Babuškov
2
我理解你的痛苦,但是现在Node还是很前沿的技术,这种情况有时难以避免。 - Kristian
7
MemoryStore 不包含在 Node 中,它包含在 Express/Connect 中。 - Mustafa
但是为什么会有这个“共识”呢?是因为它可以跨进程(节点集群)工作吗? - George

6
使用Redis或Mongo作为存储的替代方案。对于Mongo,您可以使用express-session-mongo模块。
还有一个建议是使用索引选项来删除过期的会话:
var MongoStore = require('express-session-mongo');
app.use(express.session({ store: new MongoStore() }));

db.sessions.ensureIndex( { "lastAccess": 1 }, { expireAfterSeconds: 3600 } )

由于数据库本身会删除过期的会话,因此Express会话不需要自行处理清除工作。
编辑:似乎您需要拥有自己的“lastAccess”字段。当您访问它时,您自己更新该字段。请查看MongoDB文档expire-data http://docs.mongodb.org/manual/tutorial/expire-data/ 编辑2:
现在变成了db.sessions.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 } ) Mongo背景线程检查此字段的时间间隔为60秒。因此删除文档的时间并不准确。

2

对于那些在使用Redis时遇到困难的人,可以尝试以下方法 - 希望这能帮到你。

我正在使用Redis进行DEV和PROD,并针对Express v4。在Windows上,我使用轻量级的MSOpenTech Redis v3.0工具集,否则,我只需使用Heroku Redis插件即可。到目前为止,让它通过Node工作并不太难...

var session = require('express-session');

. . .

var RedisStore = require('connect-redis')(session);

var redisClient = require('redis').createClient(process.env.REDIS_URL);

var redisOptions = { 
        client: redisClient, 
        no_ready_check: true,
        ttl: 600,
        logErrors: true
};

var redisSessionStore = new RedisStore(redisOptions);

app.use(session({
    store: redisSessionStore,
    secret: 'Some.Long.Series.of.Crazy.Words.and.Jumbled.letter.etc',
    resave: true,       
    saveUninitialized: true 
}));

祝你好运!

PS:我刚刚重新阅读了原始问题并注意到这一点——抱歉!

为这个简单的任务安装Redis、MongoDB或其他数据库似乎过于浪费。


0

如果你使用的是OSX,

brew install memcached

如果是Linux

apt install memcached

解决会话消息问题,因为应用程序无法连接到127.0.0.1:11211的memcache服务。


0

使用

import createMemoryStore from "memorystore";
...
// Memory store
const MemoryStore = createMemoryStore(session);
app.use(session({
    ...
    store: new MemoryStore({
        checkPeriod: 86400000 // prune expired entries every 24h
    }),
    ...
}))

修复我的问题


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