环境:

JDK版本:1.7.0_80
Tomcat版本:8.0
zookeeper版本:3.4.8

问题:

2018-11-13  18:33:23.598 [localhost-startStop-1-SendThread(mt-zookeeper-vip.100.idc:2181)] WARN  org.apache.zookeeper.ClientCnxSession closed, press ENTER to start, l to last session or q to exit.c./x.x.x.x:2181, unexpected error, closing socket connection and attempting reconnect
java.io.IOException: Unreasonable length = 1256440
    at org.apache.jute.BinaryInputArchive.checkLength(BinaryInputArchive.java:127)
    at org.apache.jute.BinaryInputArchive.readBuffer(BinaryInputArchive.java:92)
    at org.apache.zookeeper.proto.GetDataResponse.deserialize(GetDataResponse.java:54)
    at org.apache.zookeeper.ClientCnxn$SendThread.readResponse(ClientCnxn.java:839)
    at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:94)
    at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:366)
    at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1141)

从上面报错信息来看,我们在BinaryInputArchive.java类中127行找到相应代码,并贴出相关代码如下:

static public final String UNREASONBLE_LENGTH= "Unreasonable length = ";
static public final int maxBuffer = Integer.getInteger("jute.maxbuffer", 0xfffff);

public byte[] readBuffer(String tag) throws IOException {
    int len = readInt(tag);
    if (len == -1) return null;
    checkLength(len);
    byte[] arr = new byte[len];
    in.readFully(arr);
    return arr;
}

// Since this is a rough sanity check, add some padding to maxBuffer to
// make up for extra fields, etc. (otherwise e.g. clients may be able to
// write buffers larger than we can read from disk!)
private void checkLength(int len) throws IOException {
    if (len < 0 || len > maxBuffer + 1024) {
        throw new IOException(UNREASONBLE_LENGTH + len);  //这里是代码第127行
    }
}

推测结论:

zookeeper服务器端默认读取数据节点大小为1.01M,当zookeeper客户端读取的节点大小超过该值时就会抛出异常信息:java.io.IOException: Unreasonable length = 1256440

注:一般情况下zookeeper服务端数据节点大小也是1M,此处由于之前碰到过节点超过1M时无法写入的问题,所以在zookeeper服务端提前设置了大小为3M。但是zookeeper客户端这边读取大小没有设置大小,所以才会出现客户端读取超大节点时抛出异常信息问题。

解决方案:

通过zookeeper源码可以看出,zookeeper会优先读取系统变量:jute.maxbuffer,当系统中没有设置该变量时,才会使用默认值大小来读取数据节点。所以,我们需要在系统变量中设置jute.maxbuffer变量值。具体设置方法如下:

在Tomcat bin目录中的catalina.sh文件中,添加JAVA_OPTS变量即:JAVA_OPTS="-Djute.maxbuffer=3145728",这里设置大小为3M

注:此处只是临时解决方案,目的是着重介绍整个解决过程(毕竟zookeeper数据信息过长,已经超出了zookeeper的使用场景。后期会考虑其他更优方案来代替)。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐