Redis发布订阅不校验消息类型,业务需自行约定序列化协议 简单来说,Redis的发布订阅(Pub/Sub)机制本身,对消息内容是完全“无感”的。它就像一个只管搬运、不管验货的传送带。这意味着,消息类型的定义、校验和解析,完全落在了业务开发者的肩上。在Spring Boot这类框架中,如果使用不当,

简单来说,Redis的发布订阅(Pub/Sub)机制本身,对消息内容是完全“无感”的。它就像一个只管搬运、不管验货的传送带。这意味着,消息类型的定义、校验和解析,完全落在了业务开发者的肩上。在Spring Boot这类框架中,如果使用不当,很容易踩坑。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
必须明确一个核心事实:Redis的 PUBLISH 和 SUBSCRIBE 命令,底层处理的只是字节数组。它不关心你塞进去的是结构化的JSON、高效的Protobuf,还是简单的纯文本。对它而言,一切都是 byte[],进进出出,仅此而已。
所以,我们常说的“自定义消息类型”,其实是业务层通过事先约定好的序列化与反序列化规则“模拟”出来的。Redis本身既不识别,更不会做任何验证。
这直接导致了一个现象:假设你向 redis_channel 这个频道,先后发送了一个JSON对象 {"name":"Alice"}、一个整数 42,甚至是一段二进制数据 0x01 0x02,Redis会照单全收,并原封不动地转发给所有订阅者。订阅方拿到这一串字节后,必须自己判断:“这次传来的,到底该用哪种方式解码?”
StringRedisTemplate默认只处理字符串这里有个新手高频踩坑区。很多开发者会下意识地直接调用 stringRedisTemplate.convertAndSend("topic", obj),如果这里的 obj 是一个自定义对象(比如 Student),那么发出去的消息体会变成该对象默认的 toString() 结果,类似 "com.example.Student@1a2b3c"。这种字符串,接收方根本无法进行有效的反序列化。
正确的做法需要三步走:
JSONUtil.toJsonStr() 或 Spring Boot 内置的 ObjectMapper.writeValueAsString() 将对象转换为JSON字符串。new String(message.getBody()) 就了事。必须先确认这串字节是UTF-8编码的合法JSON字符串。StudentEvent,而拒绝 UserEvent),一个常见的实践是在消息体中加入一个 type 字段。消费者先解析出这个字段,再做分支判断,比如 if ("student".equals(json.get("type")))。MessageListener反序列化时要防空指针和格式异常在消息监听器 MessageListener 的 onMessage 方法中,一行看似简单的 new String(message.getBody()),其实暗藏至少三个“陷阱”:
message.getBody() 理论上可能返回 null(虽然罕见,但在极端网络抖动或客户端实现异常时可能发生)。new String(...) 若不指定字符集,会使用系统默认编码。这在Linux和Windows环境间可能不一致,导致乱码。务必显式指定为 StandardCharsets.UTF_8。onMessage 方法中抛出异常,默认情况下Spring会静默处理,导致消息“凭空消失”却无任何错误日志,问题排查极其困难。因此,一个健壮的监听器实现必须包裹完整的异常处理逻辑。来看一个示例片段:
public void onMessage(Message message, byte[] pattern) {
try {
String body = new String(message.getBody(), StandardCharsets.UTF_8);
Student event = JSONUtil.toBean(body, Student.class);
log.info("收到学生事件:{}", event.getName());
} catch (Exception e) {
log.error("解析消息失败,channel={}, raw={}",
new String(message.getChannel()),
new String(message.getBody(), StandardCharsets.UTF_8), e);
}
}
当消息的生产者和消费者使用不同编程语言时,挑战会进一步升级。即使都约定使用JSON,细节上的差异也可能导致解析失败。
例如,Ja va服务使用Fastjson库序列化一个包含 Date 字段的对象,输出可能是 "2026-04-01T05:30:00";而Go服务使用标准库 encoding/json 解析时,可能期望的是 "2026-04-01 05:30:00" 这种格式。格式不匹配,反序列化就会出错。
要解决这类问题,需要建立严格的约定:
"2026-04-01T05:30:00Z")来处理所有时间字段。Map 这类泛型结构作为消息体,改用明确定义字段的DTO类或结构体,约束性更强。.proto 文件生成代码,并且版本严格保持一致。实际开发中,最棘手的问题往往不是主流服务间的对接,而是临时接入的脚本或服务。比如,某天一个Python脚本临时接入,它使用 json.dumps(dict, ensure_ascii=False) 序列化数据,可能无意间引入了额外的空格或特殊字符。这种微小的差异,就可能导致Ja va端的解析器静默失败,日志里只留下一句令人困惑的“空消息”,排查起来费时费力。
所以说,在Redis Pub/Sub的实践中,把序列化协议当作一项重要的“服务间契约”来严格管理,是保证系统稳定性的关键一步。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述