bug描述
Avro 序列化 Event长度超过63后 反序列化失败
问题定位
1.程序中将avro序列化后的byte数组转了String又转了byte[]然后进行反序列化,在len>63时 两个byte[]出现了不一致
byte[] b = new String(f.serializeEvent(e, true)).getBytes()
初步判断编码方式的问题导致了byte数组的不一致
经过网上查询和亲自实践,在默认UTF-8编码下 如果byte有小于0的值,UTF-8会将它转为[char]0xFFFD 再次转byte时转为[-17,-65,-67]
关键代码如下
//CharsetDecoder line 229
protected CharsetDecoder(Charset cs,
float averageCharsPerByte,
float maxCharsPerByte)
{
this(cs,
averageCharsPerByte, maxCharsPerByte,
"\uFFFD");
}
// UTF_8.class line 393
else {
if (this.malformedInputAction() != CodingErrorAction.REPLACE) {
return -1;
}
if (var2 >= var5 || !isMalformed3_2(var9, var1[var2])) {
var4[var6++] = this.replacement().charAt(0);
return var6;
}
var4[var6++] = this.replacement().charAt(0);
}
UTF_8.class line 559
var6[var7++] = (byte)(224 | var10 >> 12);
var6[var7++] = (byte)(128 | var10 >> 6 & 63);
var6[var7++] = (byte)(128 | var10 & 63);
如果不将byte[]转String 再转byte[]问题可以得到解决
但是为什么长度会影响呢?
我们查看序列化的代码 序列化bytes类型时代码如下
// BinaryEncoder.java line 59
public void writeBytes(ByteBuffer bytes) throws IOException {
int len = bytes.limit() - bytes.position();
if (0 == len) {
writeZero();
} else {
writeInt(len);
writeFixed(bytes);
}
}
根据问题描述 应该出在写入长度上
查看代码
// DirectBinaryEncoder.java line 72
public void writeInt(int n) throws IOException {
int val = (n << 1) ^ (n >> 31);
if ((val & ~0x7F) == 0) {
out.write(val);
return;
} else if ((val & ~0x3FFF) == 0) {
out.write(0x80 | val);
out.write(val >>> 7);
return;
}
int len = BinaryData.encodeInt(n, buf, 0);
out.write(buf, 0, len);
}
首先 将len乘以2赋值到val
判断 val是否小于128(这就是63存在的原因) 大于128进入第一个else
将byte符号位置1后写入 这时候 就写入了负值 导致了问题的产生!
问题解决
- 不使用byte转string再转回来
- 使用ISO8859-1编码方式进行转换
网友评论