【Netty】Netty 编码器(十三)

news2024/11/22 16:00:16

文章目录

  • 前言
  • 一、MessageToByteEncoder 抽象类
  • 二、MessageToMessageEncoder 抽象类
  • 总结

前言

回顾Netty系列文章:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)
  • Netty ChannelHandler(四)
  • ChannelPipeline源码分析(五)
  • 字节缓冲区 ByteBuf (六)(上)
  • 字节缓冲区 ByteBuf(七)(下)
  • Netty 如何实现零拷贝(八)
  • Netty 程序引导类(九)
  • Reactor 模型(十)
  • 工作原理详解(十一)
  • Netty 解码器(十二)

编码器就是用来把出站数据从一种格式转换到另外一种格式,因此它实现了ChannelOutboundHandler,类似于解码器,Netty 也提供了一组类来帮助开发者快速上手编码器,当然,这些类提供的是与解码器相反的方法,如下所示:

  • 编码从消息到字节(MessageToByteEncoder)。
  • 编码从消息到消息(MessageToMessageEncoder)。

一、MessageToByteEncoder 抽象类

在上一篇文章中,我们知道了如何使用ByteToMessageDecoder来将字节转换成消息,现在可以使用MessageToByteEncoder实现相反的效果。

MessageToByteEncoder 核心代码如下:

public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
    private final TypeParameterMatcher matcher;
    private final boolean preferDirect;

    protected MessageToByteEncoder() {
        this(true);
    }

    protected MessageToByteEncoder(Class<? extends I> outboundMessageType) {
        this(outboundMessageType, true);
    }

    protected MessageToByteEncoder(boolean preferDirect) {
        this.matcher = TypeParameterMatcher.find(this, MessageToByteEncoder.class, "I");
        this.preferDirect = preferDirect;
    }

    protected MessageToByteEncoder(Class<? extends I> outboundMessageType, boolean preferDirect) {
        this.matcher = TypeParameterMatcher.get(outboundMessageType);
        this.preferDirect = preferDirect;
    }

    public boolean acceptOutboundMessage(Object msg) throws Exception {
        return this.matcher.match(msg);
    }

    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf buf = null;

        try {
            if (this.acceptOutboundMessage(msg)) {
                I cast = msg;
                buf = this.allocateBuffer(ctx, msg, this.preferDirect);

                try {
                    this.encode(ctx, cast, buf);
                } finally {
                    ReferenceCountUtil.release(msg);
                }

                if (buf.isReadable()) {
                    ctx.write(buf, promise);
                } else {
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }

                buf = null;
            } else {
                ctx.write(msg, promise);
            }
        } catch (EncoderException var17) {
            throw var17;
        } catch (Throwable var18) {
            throw new EncoderException(var18);
        } finally {
            if (buf != null) {
                buf.release();
            }

        }

    }

    protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, I msg, boolean preferDirect) throws Exception {
        return preferDirect ? ctx.alloc().ioBuffer() : ctx.alloc().heapBuffer();
    }

    protected abstract void encode(ChannelHandlerContext var1, I var2, ByteBuf var3) throws Exception;

    protected boolean isPreferDirect() {
        return this.preferDirect;
    }
}

在MessageToByteEncoder抽象类中,唯一要关注的是encode方法,该方法是开发者需要实现的唯一抽象方法。它与出站消息一起调用,将消息编码为ByteBuf,然后,将ByteBuf转发到ChannelPipeline中的下一个ChannelOutboundHandler。

以下是MessageToByteEncoder 的使用示例:

public class ShortToByteEncoder extends MessageToByteEncoder<Short> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception {
        out.writeShort(msg);//将Short转成二进制字节流写入ByteBuf中
    }
}

上述示例中,ShortToByteEncoder收到 Short 消息,编码它们,并把它们写入ByteBuf。然后,将ByteBuf转发到ChannelPipeline中的下一个ChannelOutboundHandler,每个 Short 将占有 ByteBuf 的两个字节。
实现ShortToByteEncoder主要分为以下两步:

  • 实现继承自MessageToByteEncoder。
  • 写 Short 到 ByteBuf。

上述的例子处理流程图如下:
在这里插入图片描述

Netty 也提供了很多MessageToByteEncoder类的子类来帮助开发者实现自己的编码器,例如:
在这里插入图片描述

二、MessageToMessageEncoder 抽象类

MessageToMessageEncoder 抽象类用于将出站数据从一种消息转换为另一种消息。
核心源码如下:

public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {

    private final TypeParameterMatcher matcher;

    /**
     * Create a new instance which will try to detect the types to match out of the type parameter of the class.
     */
    protected MessageToMessageEncoder() {
        matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I");
    }

    /**
     * Create a new instance
     *
     * @param outboundMessageType   The type of messages to match and so encode
     */
    protected MessageToMessageEncoder(Class<? extends I> outboundMessageType) {
        matcher = TypeParameterMatcher.get(outboundMessageType);
    }

    /**
     * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next
     * {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.
     */
    public boolean acceptOutboundMessage(Object msg) throws Exception {
        return matcher.match(msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        CodecOutputList out = null;
        try {
            if (acceptOutboundMessage(msg)) {
                out = CodecOutputList.newInstance();
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                try {
                    encode(ctx, cast, out);
                } finally {
                    ReferenceCountUtil.release(cast);
                }

                if (out.isEmpty()) {
                    throw new EncoderException(
                            StringUtil.simpleClassName(this) + " must produce at least one message.");
                }
            } else {
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable t) {
            throw new EncoderException(t);
        } finally {
            if (out != null) {
                try {
                    final int sizeMinusOne = out.size() - 1;
                    if (sizeMinusOne == 0) {
                        ctx.write(out.getUnsafe(0), promise);
                    } else if (sizeMinusOne > 0) {
                        // Check if we can use a voidPromise for our extra writes to reduce GC-Pressure
                        // See https://github.com/netty/netty/issues/2525
                        if (promise == ctx.voidPromise()) {
                            writeVoidPromise(ctx, out);
                        } else {
                            writePromiseCombiner(ctx, out, promise);
                        }
                    }
                } finally {
                    out.recycle();
                }
            }
        }
    }

    private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList out) {
        final ChannelPromise voidPromise = ctx.voidPromise();
        for (int i = 0; i < out.size(); i++) {
            ctx.write(out.getUnsafe(i), voidPromise);
        }
    }

    private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) {
        final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
        for (int i = 0; i < out.size(); i++) {
            combiner.add(ctx.write(out.getUnsafe(i)));
        }
        combiner.finish(promise);
    }


    protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
}

同MessageToByteEncoder抽象类一样,MessageToMessageEncoder 唯一要关注的也是encode方法,该方法是开发者需要实现的唯一抽象方法。对于使用write()编写的每条消息,都会调用该消息,以将消息编码为一个或多个新的出站消息,然后将编码后的消息转发。
下面是使用 MessageToMessageEncoder 的一个例子:

public class IntegerToStringEncoder extends MessageToMessageEncoder <Integer> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
        out.add(String.ValueOf(msg));
    }
}

上述示例将 Integer 消息编码为 String 消息,主要分为两步:

实现继承自MessageToMessageEncoder。
将Integer 转为 String,并添加到 MessageBuf。

上述例子的处理流程如下图所示:

在这里插入图片描述

总结

通过上述文章的讲解,我们对于编码器和解码器应该都有了一定的认识,其实针对编码和解码,Netty 还提供了第三种方式,那就是编解码器。下节我们就来讲解一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/575454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

notepad++查询指定内容并复制

背景说明 记录一下使用notepad进行文本内容查找以及替换的相关场景,简单记录方便后期查看,场景内容: 1.从指定的给出内容中筛选出所有的人员id集合 2.将每一行后面添加逗号 1.从指定的给出内容中筛选出所有的人员id集合 要求从指定的给出内容中筛选出所有的人员id集…

便携式明渠流量计的使用特点

便携式明渠流量计使用特点&#xff1a; 便携式明渠流量计使用特点&#xff0c;首先了解相关要求&#xff1a; 随着新标准JJG711-1990《明渠堰槽流量计试行检定规程》、HJ/T15-2019《超声波明渠污水流量计技术要求及检测方法》、HJ 354-2019《水污染源在线监测系统(CODCr、NH3-N…

Java开发 - 你不知道的JVM优化详解

前言 代码上的优化达到一定程度&#xff0c;再想提高系统的性能就很难了&#xff0c;这时候&#xff0c;优秀的程序猿往往会从JVM入手来进行系统的优化。但话说回来&#xff0c;JVM方面的优化也是比较危险的&#xff0c;如果单单从测试服务器来优化JVM是没有太大的意义的&…

制作PE工具盘

文章目录 前言一、什么是PE工具箱&#xff1f;二、制作WinPE工具箱例&#xff1a;制作ISO模式的PE工具箱 三、PE工具箱的典型应用1.清除Windows登陆密码2.调整分区大小3.系统备份4.系统恢复例&#xff1a;系统备份与恢复 四、使用U深度制作的PE工具恢复误删除的文件实验步骤注意…

springboot+vue车辆充电桩管理系统(java项目源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的车辆充电桩管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;…

Vim的使用

1.什么是Vim Vim是Linux下的一款代码编辑器&#xff0c;vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅…

Javascript中常用方法

1.操作元素的类&#xff1a; 元素.classList.add(类名) 元素.classList.remove(类名) 元素.classList.toggle(类名) //切换类名 2.操作表单元素属性&#xff1a; 表单.value用户名 表单.typepassworddisabled checked selected 3.自定义属性&#xff1a; 定义&#xff1a…

小程序极速注册认证免300认证费 突破管理员身份只能绑定5个小程序绿色通道

小程序注册账号管理相信大家有时候头都很大&#xff0c;一个身份证号只能绑定5个小程序主管理员。超个5个小程序就得请朋友、请亲戚身份证绑定管理员。有时还得看对方不方便&#xff0c;改个类目改个LOGO都还得管理员扫码。为了满足会员需求同是也满足自己需要&#xff0c;特别…

C语言初阶 牛客网刷题笔记(将持续更新..)

BC7 缩短二进制 printf 格式控制符 “%o” —— 八进制整数 、“%x” —— 十六进制整数 &#xff1b; 修饰符 “#” —— 控制前导显示 BC64 K形图案 错因&#xff1a;把图形分成两部分&#xff0c;下半部分打印错误 先把下半部分作为一个完整三角形&#xff1a; int n0;scan…

2023新版Spring6全新讲解-核心内容之AOP

Spring核心之AOP 一、前置基础-代理模式 在学习Spring的AOP之前我们需要补充下设计模式中的代理模式。这块是理解AOP的必备基础内容。 1. 静态代理 若代理类在程序运行前就已经存在&#xff0c;那么这种代理方式被成为 静态代理 &#xff0c;这种情况下的代理类通常都是我们在J…

ESG成全球风潮,联想造了一个可持续的“进托邦”

不得不承认&#xff0c;全球经济前景仍然存在较大的不确定和挑战。全球经济疲软、地缘政治逆风、行业竞争加剧等多重压力让很多人都感受到了寒意。 在可预见的未来&#xff0c;我们将继续在一个复杂多变的全球环境中运营。 因此&#xff0c;著名的科技思想家凯文凯利提出&#…

PCB的层间结构、铜箔厚度选择、PCB纵横比和板厚的要求

PCB的层间结构 a) 原则上应该采用对称结构设计。对称的含义包括&#xff1a;介质层厚度及种类、铜箔厚度、图形分布类型&#xff08;大铜箔层、线路层&#xff09;的对称。 b) 考虑电压击穿问题&#xff0c;正常情况下推荐介质层厚度设计值为≥0.1mm。 铜箔厚度选择 选择铜箔…

javascript基础六:说说你对闭包的理解?闭包使用场景?

一、是什么 一个函数和对其周围状态&#xff08;lexical environment&#xff0c;词法环境&#xff09;的引用捆绑在一起&#xff08;或者说函数被引用包围&#xff09;&#xff0c;这样的组合就是闭包&#xff08;closure&#xff09; 也就是说&#xff0c;闭包让你可以在一个…

机器学习-Kmeans

K-means是一种经典的无监督学习算法&#xff0c;用于对数据进行聚类。K-means算法将数据集视为具有n个特征的n维空间&#xff0c;并尝试通过最小化簇内平方误差的总和来将数据点划分为簇。本文将介绍K-means算法的原理、实现和应用。 定义 K-means是一种无监督学习算法&#…

MongoDB基础到入门(一篇就够了)

文章目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨前言MongoDBMongoDB体系结构MongoDB数据模型MongoDB部署安装服务器启动服务器 Shell连接(mongo命令)MongoDB可视化工具MongoDB命令基本常用⭐权限数据库⭐辅助命令⭐集合⭐…

vite的使用

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版&#xff0c;配图更多&#xff0c;CSDN博文图片需要手动上传&#xff0c;因此文章配图较少&#xff0c;看不懂的可以去菜鸡博客参考一下配图&#xff01; 系列文章目录 前端系列文章——传送门 后端系列文章——传送…

抖音seo源代码分享(前端+后端)

后端代码展示&#xff1a; $where [ [name > dvtv_s_id, oper > , value > $this->sid], [name > dvtv_dv_id, oper > , value > $dv_id], ]; $cache_model new App_Model_Douyin_MysqlVideoTempVideoStora…

挂耳式耳机推荐,这几个蓝牙耳机品牌不容错过!

办公的同时享受音乐是释放工作压力的不错途径&#xff0c;对于成为打工人日常配饰的耳机随着无线技术的不断进步也在不断开发新产品。开放式耳机不入耳佩戴有效的降低对耳朵的负担&#xff0c;在不打扰旁人的同时&#xff0c;长时间的佩戴也是对耳朵的考验&#xff0c;不知该如…

29 Vue 中 v-if/show/for 的实现

前言 这是最近的碰到的那个 和响应式相关的问题 特定的操作之后响应式对象不“响应“了 引起的一系列的文章 主要记录的是 vue 的相关实现机制 呵呵 理解本文需要 vue 的使用基础, js 的使用基础 v-if 测试用例 测试用例如下, 主要是一个 if 的使用 这里我们仅仅跟进到…

chatgpt赋能python:Python桌面应用程序:在SEO中的重要性和应用

Python桌面应用程序&#xff1a;在SEO中的重要性和应用 在当今数字时代&#xff0c;拥有一个桌面应用程序成为了非常重要的一件事情&#xff0c;特别是对于那些需要使用软件来完成日常任务的工作人员。而Python作为一种跨平台编程语言&#xff0c;可以帮助开发者编写适用于Win…