• 如何解决Redis序列化对象乱码的问题
  • 发布于 2个月前
  • 432 热度
    0 评论
一. 引言
最近看到一个这样的项目,涉及设备、云端、APP三端。其流程图如下:

其工作流程:在设备端点播歌曲的时候,其会把播放的歌曲播放信息上报的云端存储起来;APP定时轮训去获取云端缓存的状态。状态包括,歌曲信息,暂停、播放中、停止。根据业务需求,上报的播放状态云端并不需要持久化存储起来,属于实时展示类信息,只需提供给前端展示就可以。因此在后端的技术选型中,选择了Redis作为存储状态的数据库。

二. Redis序列化配置
为了在云端使用Redis,建立了一个RedisUtil的工具类:
@Service
public class RedisUtil<T> {
    
    private RedisTemplate<String, T> redisTemplate;
    private ValueOperations<String, T> valueOperations;

    public RedisUtil(RedisTemplate<String, T> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.valueOperations = redisTemplate.opsForValue();
    }
 // 堆代码 duidaima.com   
 // 设置方法,通过该方法可以往Redis中放值
    public void put(String key, T value) {
        this.valueOperations.set(key, value);
    }
 // 通过该方法可以获取key对于的value
    public T get(String key) {
        return this.valueOperations.get(key);
    }
}
如上所示,在RedisUtil添加了put和get方法,用于存放数据和获取数据。这样配置完后,就可以通过在使用的地方注入该类即可使用其方法。但是,在后续的排查Bug的时候就遇到了难题,默认情况下RedisTemplate使用的默认的序列化方法。

序列化简介:
序列化是将数据结构或对象转换为一系列字节,以便可以将其存储在文件或内存缓冲区中,或通过网络传输。序列化的过程包括将数据结构中的字段值转换为字节表示,并在必要时将它们压缩。
if (this.keySerializer == null) {
    this.keySerializer = this.defaultSerializer;
    defaultUsed = true;
}
如源码所示,当没有指定序列化时,使用的默认方法;其默认是一个JDK自带的序列化器。这个序列化器可以将任何实现了java.io.Serializable接口的对象序列化成字节数组,然后存储到Redis中。

但该方法存在一些缺点:
1.序列化后的字节数组比较大
2.序列化和反序列化的速度较慢
除了性能的原因,该序列的方法是将对象转为字节数组;字节数组是由一组字节(8位二进制数)组成的数据类型,通常用于在计算机系统中传输和存储数据。由于字节是二进制数,因此字节数组通常以十六进制表示。并且可能出现一些乱码,比如:原本在程序里面的key为:playback:status:182518930214498。被序列化后为:
"  \u0000\u0005t\u0000-playback:status:182518930214498"
在存储时,RedisTemplate自己添加了一些乱码。
这些乱码给开发者带来很大不便,不能通过显示调用get去获取对应的value。
因此,为了实现自定义序列化方式,需要在项目中配置序列化方式,如下所示。
@Configuration
public class Config{
 @Bean
    RedisUtil<Object> redistUtil(RedisConnectionFactory factory) {
        // 堆代码 duidaima.com
        // 初始化RedisTemplate
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        //  采用UTF-8实现字符串序列化方法, 用户key
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 对value进行序列化
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置连接
        template.setConnectionFactory(factory);
        // 设置序列化方法
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 当template进行实例化后,对template进行初始化
        template.afterPropertiesSet();
        return new RedisDao<Object>(template);
    }
}

在如上的代码中,实例化了template,并且使用StringRedisSerializer对key进行序列化;使用GenericJackson2JsonRedisSerializer对value进行序列化。在最后,使用了afterPropertiesSet方法初始化 Redis 连接池、RedisTemplate 等相关配置。这样可以保证 Redis 相关的配置在 bean 实例化后都已经完成。


通过以上的配置,在获取key的时候就能清晰展示,并且其value值经过序列化后,也可以请求看到里面的数据。就能实现key为:playback:status:182518930214498,和程序里面的key对应起来,方便日志排查。


四. 小结
综上所述,在使用RedisUtil的时候,需要首先去配置序列化,方便通过指令查询Redis数据库中的数据。让基础工具类好用,无坑。
用户评论