错误案例
这几天生意经的taglist的页面上一直存在缓存不一致的情况,而在本地调试始终不能重现线上的问题。
系统中出现了这样一种情况:在我们新增“doclist_84_饮食健康”的静态位的时候提示已经存在,但在memcached中并不存在。
查看代码发现在把一个key放到Memcached cache中的时候经过了下面的过程.
Object (key) -> 调用对象的toString方法 -> String.getBytes -> 将字节数组md5编码 -> newKeyString
代码如下
public Object get(Object key) throws CacheException { return manager.get(getKey(key)); } private String getKey(Object key) { if (key == null) { throw new IllegalArgumentException("Cache key not be null"); } MessageDigest alga; try { alga = MessageDigest.getInstance("SHA"); alga.update(region.getBytes()); alga.update(String.valueOf(key).getBytes()); byte[] digest = alga.digest(); return new String(Hex.encodeHex(digest)); } catch (NoSuchAlgorithmException e) { // nothing } return null; }
上线后发现 “doclist_84_饮食健康” 和 “doclist_84_饮食健康”编码后就都变成了 “doclist_84_????”。
错误分析
java的String.getBytes的时候,默认是按照系统的编码方式对字符串进行编码,系统编码方式不明就按照“ISO-8859-1”方式对字符串编码。 这就导致线上服务器使用 “ISO-8859-1”方式对字符串编码, 这样 Jdk中StringCoding的代码:
static byte[] encode(String charsetName, char[] ca, int off, int len) throws UnsupportedEncodingException { StringEncoder se = (StringEncoder)deref(encoder); String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
因此建议memcached中存放的key最好不要使用中文,防止由于系统编码不同而导致故障。
正确用法
从这次事件的到的教训是:
1.在涉及到中文存储的时候,需要考虑到中文编码可能带来的影响。
2.Linux, windows的不同环境可以导致不同的测试结果, 如果涉及到编码问题,最好在两个系统下都测试一下。
在二方库 com.alibaba.china.biz.cache.store.CacheStore.MemcachedStore类中调用String 的getBytes())的时候指定编码,例如utf8。
测试关注点
Jdk中 String类的getBytes方法的编码问题:有些编码是同环境相关的,在windows系统上可能发现不了问题。 这需要测试在做测试能模拟出生产环境