java netty之MessageToByteEncoder

本文介绍Netty中的MessageToByteEncoder,它的作用是将用户自定义的类型转换为字节类型以便发送。MessageToByteEncoder继承自messageadapter,通过重写encode方法,数据在flush时被转发到下一个handler的缓冲区,最终通过默认的outboundhandler发送出去。理解这个过程对于构建Netty管道至关重要。

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

上面的文章已经说过了decoder,它的用处是将读取的byte数据转化为用户自己定义的数据类型

这一篇文章要说的encoder,它的作用与前面提到的decoder就相反了,用于将用户定义的类型转化为byte类型,这样才能通过channel发送出去。。。

我们还是先来看看它的继承体系吧:


可以看到它继承自messageadapter,这种类型我们在前面已经有过说明,adapter的作用就是比较粗糙的实现方法,用户可以继承它然后重写自己感兴趣的方法,

而且在前面我们知道在pipeline上面调用write方法的时候,netty会从pipeline的后面向前寻找合适的outboundhandler用于处理要写的数据,而且是先将数据存放到handler的buffer里面,真正的写数据则是调用flush方法实现的。。。

首先我们要来看看这里将会调用的flush方法:

    @Override
    public final void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        ChannelHandlerUtil.handleFlush(ctx, promise, isCloseOnFailedFlush(), this);
    }
这里调用了ChannelHandlerUtil的flush方法,我们再来看看这个方法是具体怎么实现的吧:


    public static <T> void handleFlush(
            ChannelHandlerContext ctx, ChannelPromise promise, boolean closeOnFailedFlush,
            SingleOutboundMessageHandler<T> handler) throws Exception {

        MessageBuf<Object> in = ctx.outboundMessageBuffer();  //取得当前outbuffer
        final int inSize = in.size();
        if (inSize == 0) {
            ctx.flush(promise);
            return;
        }

        int processed = 0;
        try {
            if (!handler.beginFlush(ctx)) {
                throw new IncompleteFlushException(
                        "beginFlush(..) rejected the flush request by returning false. " +
                        "none of " + inSize + " message(s) fulshed.");
            }
            for (;;) {  //一个for循环将数据全部都取出来
                Object msg = in.poll();
                if (msg == null) {
                    break;
                }

                if (!handler.acceptOutboundMessage(msg)) {
                    addToNextOutboundBuffer(ctx, msg);
                    processed ++;
                    continue;
                }

                @SuppressWarnings("unchecked")
                T imsg = (T) msg;
                try {
                    handler.flush(ctx, imsg);  //这里将会调用用户定义的flush方法,将message转化为byte类型存到下一个handler
                    processed ++;
                } finally {
                    BufUtil.release(imsg);
                }
            }
        } catch (Throwable t) {
            IncompleteFlushException pfe;
            if (t instanceof IncompleteFlushException) {
                pfe = (IncompleteFlushException) t;
            } else {
                String msg = processed + " out of " + inSize + " message(s) flushed";
                if (t instanceof Signal) {
                    Signal abort = (Signal) t;
                    abort.expect(ABORT);
                    pfe = new IncompleteFlushException("aborted: " + msg);
                } else {
                    pfe = new IncompleteFlushException(msg, t);
                }
            }
            fail(ctx, promise, closeOnFailedFlush, pfe);
        }

        try {
            handler.endFlush(ctx);
        } catch (Throwable t) {
            if (promise.isDone()) {
                logger.warn("endFlush() raised a masked exception due to failed flush().", t);
            } else {
                fail(ctx, promise, closeOnFailedFlush, t);
            }
        }

        if (!promise.isDone()) {
            ctx.flush(promise);   //这里又会向上寻找outboundhandler,把数据flush出去,这里最终将会找到pipeline上面默认的headerhandler
        }
    }

上面的代码将会调用MessageToByteEncoder类型的flush方法,这个方法将会调用用户定义的encode方法,用于将数据写到下一个handler的buffer里面,最终还会调用当前handler,也就是encoder的context的flush方法,将会向上寻找outboundhandler的flush方法,其实最终会找到默认的headerhandler。。。。

好吧,我们来看看MessageToByteEncoder类型的flush方法的实现吧:
    @Override
    public void flush(ChannelHandlerContext ctx, I msg) throws Exception {
        try {
          //这里是写到下一个handler的outbuffer里面去
            encode(ctx, msg, ctx.nextOutboundByteBuffer());
        } catch (CodecException e) {
            throw e;
        } catch (Exception e) {
            throw new CodecException(e);
        }
    }

这里调用了用户定义的encode方法,不过需要注意的是这里传进去的buffer是下一个outboundhandler的outbuf,这里在复习一下前面的知识:

(1)operationhandler相关的动作都是从pipeline的后面向前进行handler的调用的

(2)在pipeline的最前面有一个netty默认的outboundhandler,它用于真正的将数据发送出去,它里面会包含一个unsafe对象,这个unsafe对象就是所属的channel的unsafe对象,最终会调用这个unsafe对象的相关方法将数据发送出去。。


如果我们要自己编写encoder,那么最终pipeline的构成如下图:


这样,当我们直接调用pipeline的write方法写我们自定义的类型的对象的时候,数据首先是写到了我们自己定义的encoder,然后当flush的时候,decoder会调用我们自己定义的方法将这些数据转化为byte类型,保存到默认的outboundhandler的buffer里面,然后再由其将数据发送出去


好了那么整个encode的流程也就走通了。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值