Redis发布订阅支持消息类型自定义吗_通过序列化与反序列化规范消息结构
Redis发布订阅不校验消息类型,业务需自行约定序列化协议

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
简单来说,Redis的发布订阅(Pub/Sub)机制本身,对消息内容是完全“无感”的。它就像一个只管搬运、不管验货的传送带。这意味着,消息类型的定义、校验和解析,完全落在了业务开发者的肩上。在Spring Boot这类框架中,如果使用不当,很容易踩坑。
Redis发布订阅本身不支持消息类型校验
必须明确一个核心事实:Redis的 PUBLISH 和 SUBSCRIBE 命令,底层处理的只是字节数组。它不关心你塞进去的是结构化的JSON、高效的Protobuf,还是简单的纯文本。对它而言,一切都是 byte[],进进出出,仅此而已。
所以,我们常说的“自定义消息类型”,其实是业务层通过事先约定好的序列化与反序列化规则“模拟”出来的。Redis本身既不识别,更不会做任何验证。
这直接导致了一个现象:假设你向 redis_channel 这个频道,先后发送了一个JSON对象 {"name":"Alice"}、一个整数 42,甚至是一段二进制数据 0x01 0x02,Redis会照单全收,并原封不动地转发给所有订阅者。订阅方拿到这一串字节后,必须自己判断:“这次传来的,到底该用哪种方式解码?”
Spring Boot中用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" 这种格式。格式不匹配,反序列化就会出错。
要解决这类问题,需要建立严格的约定:
- 时间格式统一:强制使用ISO 8601标准格式(如
"2026-04-01T05:30:00Z")来处理所有时间字段。 - 避免模糊结构:尽量不要使用
Map这类泛型结构作为消息体,改用明确定义字段的DTO类或结构体,约束性更强。 - 二进制协议需严格同步:如果选用Protobuf等二进制协议,必须确保所有语言客户端都基于同一份
.proto文件生成代码,并且版本严格保持一致。
实际开发中,最棘手的问题往往不是主流服务间的对接,而是临时接入的脚本或服务。比如,某天一个Python脚本临时接入,它使用 json.dumps(dict, ensure_ascii=False) 序列化数据,可能无意间引入了额外的空格或特殊字符。这种微小的差异,就可能导致Ja va端的解析器静默失败,日志里只留下一句令人困惑的“空消息”,排查起来费时费力。
所以说,在Redis Pub/Sub的实践中,把序列化协议当作一项重要的“服务间契约”来严格管理,是保证系统稳定性的关键一步。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
mysql如何限制单条SQL执行消耗的内存_调整sort_buffer_size与join_buffer
MySQL内存调优实战:如何精准控制单条SQL的内存消耗? 说到MySQL性能调优,sort_buffer_size和join_buffer_size这两个参数总是绕不开的话题。很多工程师的第一反应是:“调大点是不是就能快些?” 事情可没这么简单。盲目调整不仅可能毫无收益,甚至还会引发内存溢出(OO
Redis发布订阅支持消息类型自定义吗_通过序列化与反序列化规范消息结构
Redis发布订阅不校验消息类型,业务需自行约定序列化协议 简单来说,Redis的发布订阅(Pub Sub)机制本身,对消息内容是完全“无感”的。它就像一个只管搬运、不管验货的传送带。这意味着,消息类型的定义、校验和解析,完全落在了业务开发者的肩上。在Spring Boot这类框架中,如果使用不当,
SQL如何计算分组内的方差与标准差_窗口聚合函数实操
SQL中VARIANCE和STDDEV默认按样本计算(除以n-1),PostgreSQL、Oracle、Snowflake均如此;MySQL的VARIANCE()等价VAR_SAMP(),STDDEV()等价STDDEV_SAMP();SQL Server需显式用STDEV()或STDEVP()。
为什么SQL触发器在执行存储过程时不触发_排查触发器嵌套触发限制
为什么SQL触发器在执行存储过程时不触发?排查触发器嵌套触发限制 触发器调用存储过程后不触发,根本不是“不触发”,而是被嵌套层数限制拦住了 很多开发者遇到触发器“失灵”时,第一反应是检查语法或权限。但真相往往更直接:你很可能撞上了SQL Server那堵硬性的32层嵌套墙。无论是DML还是DDL触发
mysql如何高效地统计不同状态的数量_使用CountIf单次扫描
MySQL不支持COUNTIF函数,需用SUM(CASE WHEN THEN 1 ELSE 0 END)实现单次扫描多状态统计,比多次COUNT(*)更高效。 MySQL 没有 COUNTIF 函数,别白找 如果你是从Excel或者其他数据库(比如SQLite、PostgreSQL)转过来的,可
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

