目录
问题产生
本地环境(spring框架)调试一个http接口,数据从数据库获取并缓存到redis,偶现接口返回的数据有乱码,如下图
排查过程
- 去掉缓存,每次都从数据库获取,不会产生乱码
- 每次都从缓存获取,不会乱码(前提缓存的数据都是正确的)
- 只使用测试环境,不管数据从缓存还是从数据库都不会产生乱码
- 只使用本地开发环境,不管数据从缓存还是从数据库都不会产生乱码
- 测试环境和本地环境都启动,每次请求都删除缓存,让缓存重新生成,偶现乱码
- 继续排查如何生成的缓存,如下图所示,先发送了mq再将缓存放入redis
- 发送mq的过程,如下图所示,将需要发送的内容转为字节码然后发送出去
- 消费mq的过程,如下图所示,将收到的字节数据转换为String并set到redis中(测试环境和本地环境都可能消费mq)
问题分析
乍一看,这个流程没什么问题,测试环境也用了很久了,没遇到过,但是在本地开发的时候却遇到了,十分奇怪。。。由于消息的消费是不确定在测试机器还是本地机器,所以会不会是两边的消息不一样导致的呢?带着这个疑问,看了下java源码。代码中
dataStr.getBytes()
最终会调用到
而
new String(message.getData())
最终会调用到
可以看到,两段代码都使用了
String csn = Charset.defaultCharset().name();
来获取默认的编码格式来进行字符串的编码和解码
故在代码中加上log
重新部署进行测试
真相大白,如下图所示
本地日志
测试环境日志
结论
- 此问题是由字符串转换导致
- 字符串转字节码,再由字节码转字符串,两次转换的编码不同导致数据乱码
- mq的发送方都是本地开发机器,所以字符串转为字节码的编码都是本地的GBK编码
- 由于mq的消费方,是不确定的,导致有时候是本地开发机器消费有时候是测试环境机器消费,并且两边的环境的默认的编码不一样,所以就导致字节码转换为字符串的时候的编码不一样,就直接导致set进redis缓存的数据是不一样的,也就是偶现乱码
- 再次提醒大家开发环境和测试环境,一定要分开,做到环境隔离