Java向Redis Stream发送实体自动转Base64问题及解决咨询
Redis Stream发送对象出现Base64编码的问题排查与修复
问题描述
使用Java 11向Redis 7.0.8的Stream发送NicknameEdit对象,代码实现如下:
package com.dolphin.soa.post.common.mq; import com.dolphin.soa.post.model.custom.NicknameEdit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.stream.ObjectRecord; import org.springframework.data.redis.connection.stream.RecordId; import org.springframework.data.redis.connection.stream.StreamRecords; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @Component @Slf4j @RequiredArgsConstructor public class StreamMessageProducer { private final RedisTemplate<String, NicknameEdit> redisTemplate; public void sendRecord(String streamKey, String nickname) { NicknameEdit nicknameEdit = new NicknameEdit(); nicknameEdit.setNickname(nickname); ObjectRecord<String, NicknameEdit> record = StreamRecords.newRecord() .in(streamKey) .ofObject(nicknameEdit) .withId(RecordId.autoGenerate()); RecordId recordId = redisTemplate.opsForStream() .add(record); } }
实体类定义:
package com.dolphin.soa.post.model.custom; import lombok.Data; import java.io.Serializable; @Data public class NicknameEdit implements Serializable { private Long userId; private String nickname; }
但Redis中存储的数据是Base64编码格式:
{ "\"X2NsYXNz\"": "\"Y29tLmRvbHBoaW4uc29hLnBvc3QubW9kZWwuY3VzdG9tLlN5c0V2ZW50\"", "\"ZGF0YS5fY2xhc3M=\"": "\"Y29tLmRvbHBoaW4uc29hLnBvc3QubW9kZWwuY3VzdG9tLk5pY2tuYW1lRWRpdA==\"", }
原因分析
出现Base64编码的核心原因是Spring Data Redis默认使用JdkSerializationRedisSerializer序列化对象:
- 该序列化器基于JDK自带机制,会将Java对象转为字节数组
- Redis Stream存储字节数组时,会自动将其编码为Base64字符串
- 同时会额外添加类元数据字段(如
_class),这些字段也会被Base64编码,导致存储内容完全不可读
修复方案
核心解决思路是替换序列化器为JSON序列化,以下两种方案均可实现:
方案一:全局配置RedisTemplate使用JSON序列化器
创建Redis配置类,覆盖默认序列化逻辑,使用Jackson2JsonRedisSerializer处理对象序列化:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // 配置JSON序列化器 Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); // 可按需配置ObjectMapper,比如忽略空值、指定日期格式等 // ObjectMapper objectMapper = new ObjectMapper(); // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // jsonSerializer.setObjectMapper(objectMapper); // Key和HashKey使用字符串序列化 template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); // Value和HashValue使用JSON序列化 template.setValueSerializer(jsonSerializer); template.setHashValueSerializer(jsonSerializer); template.afterPropertiesSet(); return template; } }
修改Producer中的RedisTemplate类型为RedisTemplate<String, Object>或RedisTemplate<String, NicknameEdit>,重新注入即可生效。
方案二:手动将对象序列化为JSON字符串发送
如果不需要全局修改RedisTemplate,可在Producer中手动将对象转为JSON字符串,使用RedisTemplate<String, String>发送:
package com.dolphin.soa.post.common.mq; import com.dolphin.soa.post.model.custom.NicknameEdit; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.RecordId; import org.springframework.data.redis.connection.stream.StreamRecords; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @Component @Slf4j @RequiredArgsConstructor public class StreamMessageProducer { private final RedisTemplate<String, String> redisTemplate; private final ObjectMapper objectMapper; public void sendRecord(String streamKey, String nickname) { NicknameEdit nicknameEdit = new NicknameEdit(); nicknameEdit.setNickname(nickname); try { // 将对象转为JSON字符串 String jsonStr = objectMapper.writeValueAsString(nicknameEdit); // 构造MapRecord发送 Map<String, String> data = new HashMap<>(); data.put("nicknameEdit", jsonStr); MapRecord<String, String, String> record = StreamRecords.newRecord() .in(streamKey) .ofMap(data) .withId(RecordId.autoGenerate()); RecordId recordId = redisTemplate.opsForStream().add(record); } catch (Exception e) { log.error("序列化对象失败", e); } } }
这种方式下,Redis中会直接存储明文JSON字符串,无Base64编码。
是否可以关闭Base64编码直接发送明文JSON?
完全可以。通过上述两种方案将序列化方式从JDK序列化替换为JSON序列化后,Redis Stream中存储的就是可读的明文JSON内容,不会再出现Base64编码的情况。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

