Netty中ByteBuf遇到问题

在实现自定义的RPC框架时,使用Netty遇到一个问题:JobManager解析作业生成的Task信息提交到TaskManager时,序列化后的字节超过2048字节,导致在Netty解码器中出现IndexOutOfBoundsException。解决方案是检查ByteBuf的可读字节数,如果小于数据长度,则重置readerIndex,等待下一次读取。修改后的解码器能正确处理分包数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记录一下此次在使用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);
        }
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值