记录一下此次在使用Netty进行RPC的调用过程中遇到的问题,自己实现的一个作业分发的RPC框架。主要有两个角色进程:
JobManager:负责客户端作业的提交管理、解析作业信息形成一张作业图分发到相应的TaskManager下执行。
TaskManager:与JobManager通信,启动的时候自动注册到JobManager,并发送心跳到角色jm中,启动后加载相关的TaskExecutor的实现的插件,通过java的ServiceLoad方式加载。
遇到的问题是:发现客户端发送作业到JobManager后,jm解析作业生成的Task的信息提交到tm的时候序列化后的字节较大,超过2048字节。这样的话在解析器NettyDecode解析提交的任务信息时会出现第一次读取ByteBuf中的字节时超过了2048,出现异常信息:
java.lang.IndexOutOfBoundsException: readerIndex(4) + length(2301) exceeds writerIndex(2048): %s
后来网上疯狂的查资料博客帖子啥的找的方法也没有解决我的问题,但是提供了思路,猜测是传输量大的数据时应该会发生分包发送的情况,推测我这遇到的情况应该是数据也被分包发了,于是在NettyDecode中进行断点发现接受的数据长度应该为2301,但当前的ByteBuf中的大小最大为2048,所以在read的时候出现IndexOutOfBoundsException异常。
后边查资料和代码调试发现ButeBuf的缓冲区是可以动态根据上次传输的数据大小来进行下次扩容的,但是当前这一次读取的数据是有限的不完整的,即实际数据大小为2301,此次读取的最多2048个字节。
解决的方案是当前读取的数据不进行解码处理,等待下一次再次解码时重新从ByteBuf中读取即可。参考代码如下:
编码器NettyEncode
public class NettyEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(
ChannelHandlerContext context,
Object message,
ByteBuf byteBuf) throws Exception {
HessianSerializer serializer = new HessianSerializer();
byte[] data = serializer.serialize(message);
int dataLength = data.length;
byteBuf.writeInt(dataLength);
byteBuf.writeBytes(data);
}
}
修改前的解码器
public class NettyDecoder extends ByteToMessageDecoder {
private final Class<?> genericClass;
public NettyDecoder(Class<?> genericClass) {
this.genericClass = genericClass;
}
@Override
protected void decode(
ChannelHandlerContext context,
ByteBuf byteBuf,
List<Object> list) throws Exception {
int dataLength = byteBuf.readInt();
if (dataLength < 0) {
context.close();
}
byte[] data = new byte[dataLength];
byteBuf.readBytes(data);
HessianSerializer serializer = new HessianSerializer();
// Serializer serializer = RpcSerializer.getSerializerByType((byte) 1);
Object object = serializer.deserialize(data, genericClass);
list.add(object);
}
}
修改后的解码器
public class NettyDecoder extends ByteToMessageDecoder {
private final Class<?> genericClass;
public NettyDecoder(Class<?> genericClass) {
this.genericClass = genericClass;
}
@Override
protected void decode(
ChannelHandlerContext context,
ByteBuf byteBuf,
List<Object> list) throws Exception {
int dataLength = byteBuf.readInt();
if (dataLength < 0) {
context.close();
}
int readableBytes = byteBuf.readableBytes();
if (dataLength > readableBytes) {
// 待读取的数据长度大于当前ByteBuf中的数据时,
// 将reader index读取的游标置为初始值0的位置,下次重新开始读取
byteBuf.resetReaderIndex();
} else {
byte[] data = new byte[dataLength];
byteBuf.readBytes(data);
HessianSerializer serializer = new HessianSerializer();
Object object = serializer.deserialize(data, genericClass);
list.add(object);
}
}
}