前言
这些一般都是我在开发中遇到的问题,希望我收录的问题,我的思考,我的灵感能给大家带来帮助!
1、Redis 匿名对象序列化问题
最近在项目中,我创建了一个匿名的HashMap对象,并将其存储到Redis中。然而,在序列化过程中,Redis却将该对象识别为了我定义的Service类型,并在序列化后添加了 @type=Service 字段。为什么会出现这种情况呢? Redis为什么会错误地识别匿名对象的类型?
java
@SpringBootTest
public class TestClass {
@Autowired
private RedisCache redisCache;
@Test
public void test(){
redisCache.setCacheObject("lele",new HashMap<String,Object>(){
{
put("key","value");
}
});
}
}
存储到Redis之后:
【回答】:
首先我们来看几个概念:
Redis序列化机制:Redis作为一种键值存储数据库,可以存储各种数据类型,包括字符串、列表、哈希等。在存储对象时,Redis需要将对象序列化为字节流以便存储在内存或磁盘中。Redis使用一种名为RESP(REdis Serialization Protocol)的二进制安全的序列化协议来序列化数据。
对象类型识别:Redis在存储对象时,会将对象的类型信息一并存储起来,以便在反序列化时恢复对象的类型。这样做可以确保反序列化后的对象类型与原始对象类型一致,从而保证数据的完整性和正确性。
匿名对象的类型问题:匿名对象指的是在代码中创建对象时未指定对象类型的对象,比如使用Java中的匿名类或匿名内部类。在Java中,HashMap是一种常见的匿名对象。当将匿名对象存储到Redis时,Redis无法直接识别对象的类型,因为匿名对象没有明确的类型信息。因此,Redis会尝试根据对象的结构或存储上下文来猜测对象的类型。
所以在Redis错误地将匿名HashMap对象识别为了Service类型。这可能是由于Redis根据对象的结构或上下文进行类型猜测时出现了误判。由于匿名对象缺乏明确的类型信息,Redis可能会根据对象中的某些特征或存储上下文进行类型推断,导致识别错误。
解决方法:要解决这个问题,您可以考虑在存储匿名对象之前将其转换为具有明确类型信息的对象,比如使用HashMap的具体子类或自定义的POJO类。这样可以确保Redis正确地识别对象的类型,并在反序列化时恢复正确的类型。另外,您还可以尝试使用Redis的一些高级特性或配置选项来更精确地控制对象的序列化和反序列化过程,以避免类型识别错误。
java
// 声明数据类型
HashMap<String, Object> map = new HashMap<>();
map.put("key","value");
redisCache.setCacheObject("lele",map);
这样子就不会识别错误:
2、Redis @type反序列化问题
Redis这块还有一个问题,
我在lele项目中,我创建了一个 Lele 对象,并将其存储到Redis中。
然后问题来了,Redis中存储的序列化对象的 @type = "com.lele.domain.Lele"。
但是我现在需要在一个xixi的项目中使用这个Lele类,于是我在xixi的项目中创建了一个数据结构和 com.xixi.domain.Lele 一模一样的类,并通过Redis将数据拿到之后想要使用。
然后我发现Redis并不能将 @type = "com.lele.domain.Lele" 的存储的数据反序列化到 com.xixi.domain.Lele?
【回答】:
经过我的一番探究,
在Redis中,对象的序列化和反序列化通常是通过对象的类型信息来完成的。当你在lele项目中创建了一个Lele对象,并将其存储到Redis中时,Redis会将该对象序列化成一个特定格式的字符串,并在其中添加一个字段 @type 来标识对象的类型。这个字段通常是对象所属类的全限定名。
当你尝试在xixi项目中将从Redis中获取的数据反序列化为 com.xixi.domain.Lele 类型时,Redis会根据存储的数据中的 @type 字段来确定对象的类型。因此,Redis会尝试将存储的数据反序列化为 com.xixi.domain.Lele 类型的对象。
然而,如果在xixi项目中创建的 com.xixi.domain.Lele 类和在lele项目中创建的 com.lele.domain.Lele 类并不是同一个类(即使它们的结构完全相同),那么Redis就无法将存储的数据正确地反序列化为 com.xixi.domain.Lele 类型的对象。这是因为Java中的类加载机制要求两个类必须具有相同的类加载器才能被认为是同一个类,否则它们被视为不同的类。
为了解决这个问题,你需要确保在xixi项目中创建的 com.xixi.domain.Lele 类和在lele项目中创建的 com.lele.domain.Lele 类是同一个类,即它们具有相同的类加载器。这通常意味着你需要确保这两个类是由同一个项目加载的,或者将这两个类放置在同一个ClassLoader可见的位置。这样,Redis就能够正确地将存储的数据反序列化为 com.xixi.domain.Lele 类型的对象了。
【解决方案】(骚操作):
如果分布式中遇到这种特殊情况,你无法拿到这个类的类加载器或者你拿不到原来的类,你可以new一个新的类然后将json转换成字符串并且替换掉里边的 @ 字符串,就像这样:
java
List<Lele> ll =
JSONArray.parseArray(
JSON.toJSONString(
redisCache.getCacheObject("lele"))
.replace("@", ""),
Lele.class);
3、这问题不就又来了吗?@type序列化是不是效率很高?redis中如果有 @type的话,反序列化的效率会比用fastjson快很多?
【回答:】
直接给答案,其实不是,效率是一点没增加,但是Redis有这么一个字段的作用是为了准确。
在Redis中,@type字段的存在通常是为了在反序列化时能够准确地确定对象的类型。这个字段会存储对象的类的全限定名,以便在反序列化时能够根据这个信息将数据正确地转换为相应的Java对象。
就序列化效率而言,@type字段本身不会显著影响序列化的效率。它只是一个额外的字段,存储了对象的类型信息,并不会增加序列化的复杂度或消耗大量的资源。因此,从效率的角度来看,@type字段对序列化的影响可以忽略不计。
然而,在某些情况下,如果你对序列化的性能有极高的要求,可能会考虑去除 @type 字段,以减少序列化的数据量。但是这样做也会导致失去了在反序列化时准确确定对象类型的功能,可能会引入类型不匹配的问题。因此,在大多数情况下,保留 @type 字段是比较合适和安全的做法。
但是相比之下,使用FastJSON等第三方库进行序列化和反序列化时,需要额外的步骤来处理类型信息,可能会稍微增加处理的复杂性和时间开销。因此,在需要频繁进行对象存储和检索的场景下,直接利用Redis的对象序列化功能可能会更高效。
4、Redis 外部网络连接Redis
- 找到 Redis 的配置文件,通常是
redis.conf
。 - 在配置文件中找到
protected-mode
的配置项。 - 将
protected-mode
设置为no
。 - 保存并关闭配置文件。
- 重启 Redis 服务器,以使修改生效。
protected-mode
是 Redis 的安全模式设置之一,用于限制来自外部网络的访问。默认情况下,protected-mode
是启用的,这意味着只允许来自本地环境(即localhost
)的连接。
如果你想要从外部网络连接到生产环境的 Redis 服务器,你需要修改 protected-mode
设置为 no
。
总结
一定要多思考,如果人永远待在舒适圈的话,人永远不会成长。共勉
觉得作者写的不错的,值得你们借鉴的话,就请点一个免费的赞吧!这个对我来说真的很重要。૮(˶ᵔ ᵕ ᵔ˶)ა