Node.js将对象存储在Redis中。

72

事情是这样的 - 我想将本地JS(node.js)对象(flash套接字引用)存储在redis中的某个键下。当我使用简单的client.set()进行存储时,它被存储为字符串。当我尝试获取值时,我只得到[object Object] - 仅仅是一个字符串。

有没有可能让它正常工作?以下是我的代码:

  addSocket : function(sid, socket) {
    client.set(sid, socket);
  },

  getSocket : function(sid) {
    client.get(sid, function(err, reply) {
      // cant't get an object here. All I get is useless string
    });
  },

你不能将 JavaScript 的引用存储在某种形式的数据库中,因为当服务器关闭时,这些对象会消失。 - Raynos
9个回答

159

由于套接字的类型为Object,在存储之前需要将对象转换为字符串,在检索套接字时需要将其转换回对象。

您可以使用

JSON.stringify(socket) 

转换为字符串并

JSON.parse(socketstr) 

转换回对象。

编辑:

自版本2.0.0发布以来,我们可以将对象存储为Redis中的哈希。

client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234");

client.hgetall("hosts", function (err, obj) {
    console.dir(obj);
});

https://redis.io/commands/hset

https://github.com/NodeRedis/node_redis


20
这真是个好回答!“你不能做某件事”从来不是一个答案。谢谢,Shawn。 - Maziyar
3
它可以使用"sockets"吗?您能在另一台机器(甚至是同一台机器)上反序列化套接字,然后开始向其中推送数据吗? - Sergio Tulentsev
1
@moeiscool 在SO上有很多应该被接受的答案,但问题的提问者并不总是关心这一点,他们从不更改已接受的答案。应该有管理员特权来更改已接受的答案。 - Kunok
1
关于编辑的一个警告,您可以在哈希中存储多个键值对,但是这些键和值仍然是字符串。因此,包含非字符串值的任意JS对象仍然无法按原样存储,因此将它们转换为JSON可能仍然是更可取的选择。 - Abion47

61

给Downvoters:这里的上下文是SET命令和存储任意对象的能力。

不,你不能这样做。你应该接受Redis将所有东西都存储为字符串的事实(毕竟协议是基于文本的)。Redis可能会执行一些优化并将某些值转换为整数,但那是它的事情,不是你的事情。

如果你想在Redis中存储任意对象,请确保在保存之前对它们进行序列化,并在检索后进行反序列化。

虽然我不确定你是否可以使用socket对象进行这样的操作。毕竟,它们只是描述系统资源(例如TCP套接字中的一个打开连接)。如果你成功地对描述进行序列化并在另一台机器上进行反序列化,那么另一台机器将无法访问该连接。


6
不确定2012年时情况如何,但至少从2016年开始,Redis可以存储任意对象,而不是将所有东西都存储为字符串。只需要使用不同的命令/方法来使用该功能。请参考Jason Loveman的答案以获得更好的解决方案:https://dev59.com/4Goy5IYBdhLWcg3wN7e8#21490063 - JMTyler
4
@JMTyler:文档链接在哪里?Redis不能存储__任意的__对象,只能存储少数预定义的类型(如字符串、哈希表和列表等)。如果您通过SET命令进行存储,它将被存储为字符串。无法避免这种情况。 - Sergio Tulentsev
是的,SET命令存储为字符串,但这显然不是Redis的唯一命令。你不能声称SET的工作方式就是Redis的全部工作方式。根据你使用的语言/平台,hash可以表示对象的另一个词,所以我不明白你的争论在哪里。提到哈希表意味着你显然知道可以存储比简单字符串更复杂的数据。 - JMTyler
4
首先,显然,“所有东西都是字符串”这个部分是在 SET 命令的上下文中。其次,Redis 哈希不等同于 JS 对象,甚至不接近。在 Redis 中存储任意内容的唯一方法是将其序列化并使用 SET 存储(当然,需要注意所涉及的内容是否可序列化/反序列化)。 - Sergio Tulentsev
在将对象存储到缓存之前,使用JSON.stringify()将其转换为字符串对我很有帮助。 - alramdein

49
下面的解决方案并没有解决使用redis的整个重点——在集群实例之间共享数据。在Redis中存储特定于实例的ID对于尝试使用该ID的另一个实例来说是毫无意义的。
然而,有一个"hmset"方法可以使用对象作为参数调用,它将把每个对象字段设置为同一键中的单独redis字段。当您调用hgetall时,它将被转换回对象。不幸的是,我认为它不处理对象内部的嵌套对象或数组,只处理可以通过"toString()"存储其值的简单属性。
因此,像这样的一个对象:
client.hmset("myhashkey",{a:1, b:2, c:'xxx'})

这个很不错,可以通过以下方式检索

client.hmget("myhashkey", function(obj) {
   console.log(obj);
});

不是很适用于:

client.hmset("myhashkeynested",{a:1, b:2, c:'xxx', d: { d1: 'x', d2: 'y'}});

8
使用以下代码可以通过客户端获取哈希表中的所有值:client.hgetall("the-key-name", function(err,obj){//...}),而对于 hmget,需要提供字段名。 - Andrew_1510
这个回答需要更多的赞!被接受的答案是错误的。 - JMTyler
2
@JMTyler:这对于存储任意的js对象(在这种情况下是sockets)没有帮助。 - Sergio Tulentsev
这是专门用于存储JS对象的。它可以成功地存储套接字对象的所有原始类型顶级属性,因为JS中的每个对象都扩展了基本对象。最坏的情况是,如果套接字对象是嵌套的,则将其转换为非嵌套形式,仍然可以以此方式保存为对象。 - JMTyler
3
使用hgetall命令时,不幸的是ab将以字符串形式返回,这使得情况变得更加棘手。 - Danyal Aytekin

7
我发现这是一个非常有用的工具,特别是当你从API传输JSON到前端时:

node-redis-jsonify

如果您收到大量JSON数据,并且无法将其存储为特定的哈希,请在存储时对其进行字符串化,这将允许您检索整个JSON而不仅仅是“ [object Object] ”。

1
这个包是一个冗长的方式,只是简单地执行JSON.stringify和JSON.parse。请检查源代码。 - Phil

3

我认为在存储对象之前,内部会调用object.toString()方法,并将其值存储。

({a: 1}).toString() # "[object Object]"

您需要做的是使用JSON.encode和JSON.parse。

您不能存储(隐藏、二进制)引用。
否则,您可能能够建立整数和套接字之间的对应关系,并存储整数。


2
我们可以使用 JSON.stringify(obj); 将对象存储为字符串,然后使用 JSON.parse(obj); 恢复原始对象。
const obj = {
   userId: socket.user._id,
   socketId: socket.id,
};
await client.set("user", JSON.stringify(obj));
const usersData = await client.get("user");

请不要仅仅发布代码作为答案,还要提供解释您的代码是如何解决问题的。带有解释的答案通常更有帮助和更高质量,并且更有可能吸引赞同。 - Tyler2P

0

您可以使用路径参数中的“。”在redis中设置JSON值(在json.setjson.get中)

例如:

await client.json.set(TEST_KEY, '.', { example: 123 });
const value = await client.json.get(TEST_KEY, {
  path: '.',
});

-1

嗯,如果你考虑一下,JavaScript 对象就是键,它们的值可能引用其他对象,在套接字的情况下可能是原生对象。所以,如果 Redis 是外部于执行 JavaScript 的话,它如何能够存储对该对象的引用?

// a and b exist inside the executing javascript, not in the external O/S
a = {}
b = {}

var obj = {
  'one': a,
  'two': b
}

// how can redis possibly store references to obj, a or b when this application exits?
redis.set('obj', obj)

// same as..
redis.set('obj', obj.toString()) /*or*/ redis.set('obj', "" + obj)

// same as..
redis.set('obj', "[object Object]")


// the trick is to stringify the object
redis.set('obj', JSON.stringify(obj))

// same as..
redis.set('obj', "{'one':{},'two':{}}")

// here redis does not have to store the actual object references but rather a json string representing the object

// this could also work
redis.set('obj', "{'one':a,'two':b}")

// where you would then do:
obj = eval(redis.get('obj')) 
// provided that a and b have already been initialized

4
使用 eval 总是一个不好的选择。 - Mauro

-5
你可以使用类似这样的技术保存对JavaScript对象的引用。基本上,它扩展了Object.prototype(不一定需要),您可以调用radd()将自定义引用添加到rhash{}中,然后使用rget()检索对象。 "引用"是一个字符串,因此它将适合Redis。这意味着您无需.stringify()和.parse()来在数据库中放置/获取副本。但是,除非序列化,否则节点关闭时对象的数据将被销毁。
var OBJECT = Object.prototype;
OBJECT.rhash = {};
OBJECT.rset = function(id, object) {
  OBJECT.rhash[id] = object;
  return id;
};
OBJECT.rget = function(id) {
  return OBJECT.rhash[id];
};

var dog = {
  name: "Skippy",
  food: "Bacon",
  bark: function() {
    alert(this.name + "!");
  }
};

var id = OBJECT.rset("a123", dog);
var ref = OBJECT.rget("a123");
ref.bark();

9
不要修改 Object.prototype - Eric
为什么需要将其绑定到Object.prototype并创建这些get/set方法,而不是只做hash = {},hash[key] = object? - Nick Sotiros
此外,这里没有进行序列化。引用的是原始对象,而不是字符串。 - Phil

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