Jdk中 String类的getBytes方法的编码问题

错误案例

这几天生意经的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系统上可能发现不了问题。 这需要测试在做测试能模拟出生产环境


你可能感兴趣的:(java)