Netty常见的设计模式

news2024/10/5 19:16:07

简介

设计模式在软件开发中起着至关重要的作用,它们是解决常见问题的经过验证的解决方案。而Netty作为一个优秀的网络应用程序框架,同样也采用了许多设计模式来提供高性能和可扩展性。在本文中,我们将探讨Netty中使用的一些关键设计模式,以及它们在构建强大网络应用程序中的应用。

源码分析

单例模式

Netty中MqttEncoder这个编码器就用到了单例模式,它将构造函数私有化,并基于饿汉式的方式全局创建单例一个MqttEncoder 的单例 。

@ChannelHandler.Sharable
public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {

    public static final MqttEncoder INSTANCE = new MqttEncoder();

    private MqttEncoder() { }

//略

}

这使得我们后续需要使用这个编码器的话,只能使用这个全局维护的示例对象INSTANCE,避免了重复创建的开销。

   nioSocketChannel.pipeline().addLast(new StringDecoder()).addLast(MqttEncoder.INSTANCE)

对此我们将其梳理为类图,对应的如下图所示:

在这里插入图片描述

同样的Netty对于异常的管理也处理的很精细,例如ReadTimeoutException ,就是基于饿汉式的方式创建一个单例INSTANCE

public final class ReadTimeoutException extends TimeoutException {

    private static final long serialVersionUID = 169287984113283421L;

    public static final ReadTimeoutException INSTANCE = new ReadTimeoutException();

    private ReadTimeoutException() { }
}

对于ReadTimeoutException这个异常类的使用,也是全局仅用这个示例进行传播:

protected void readTimedOut(ChannelHandlerContext ctx) throws Exception {
        if (!closed) {
            ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE);
            ctx.close();
            closed = true;
        }
    }

对应的类图如下所示:

在这里插入图片描述

简单工厂模式

策略模式最典型的特点就是:

  1. 客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
  2. 基于简单工厂封装对象创建细节,由于涉及创建的对象类型较少,所以所有的创建细节都在一个方法内实现。

Netty中最典型的实现就是DefaultEventExecutorChooserFactory,这个工厂会工具用户传入executors动态创建EventExecutorChooser

@UnstableApi
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {

    public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();

    private DefaultEventExecutorChooserFactory() { }

	//根据用户传入的executors动态返回一个executors轮询选择器
    @SuppressWarnings("unchecked")
    @Override
    public EventExecutorChooser newChooser(EventExecutor[] executors) {
    	//如果executors的长度是2的次幂则返回PowerOfTowEventExecutorChooser,反之返回GenericEventExecutorChooser
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTowEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }

    private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }

    private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }
    }

    private static final class GenericEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        GenericEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }
    }
}

对应的类图如下所示:

在这里插入图片描述

装饰者模式

基于装饰者模式,实现基于拓展的方式对类进行增强,做到尽可能避免没必要的修改,在NettyWrappedByteBuf就是最经典的装饰着模式,其实现思路为:

  1. WrappedByteBuf 继承ByteBuf 获取ByteBuf 的所有行为方法。
  2. 对外开放一个构造方法,传入ByteBuf
  3. 基于第一步的继承的ByteBuf 的所有行为方法,通过传入的ByteBuf进行实现,并返回WrappedByteBuf 而不是传入的buf,从而做对外屏蔽内部实现。
class WrappedByteBuf extends ByteBuf {

    protected final ByteBuf buf;

    protected WrappedByteBuf(ByteBuf buf) {
        if (buf == null) {
            throw new NullPointerException("buf");
        }
        this.buf = buf;
    }

    @Override
    public final boolean hasMemoryAddress() {
        return buf.hasMemoryAddress();
    }

    @Override
    public final long memoryAddress() {
        return buf.memoryAddress();
    }

    @Override
    public final int capacity() {
        return buf.capacity();
    }

    @Override
    public ByteBuf capacity(int newCapacity) {
        buf.capacity(newCapacity);
        return this;
    }

    @Override
    public final int maxCapacity() {
        return buf.maxCapacity();
    }

    @Override
    public final ByteBufAllocator alloc() {
        return buf.alloc();
    }
	//使用buf实现,返回当前对象,从而对外屏蔽内部实现细节
	@Override
    public ByteBuf retain() {
        buf.retain();
        return this;
    }

//略
}

对应的类图如下所示:

在这里插入图片描述

与之同理的还有UnreleasableByteBuf ,这里就不多做赘述了:

final class UnreleasableByteBuf extends WrappedByteBuf {

    private SwappedByteBuf swappedBuf;

    UnreleasableByteBuf(ByteBuf buf) {
        super(buf);
    }

    @Override
    public ByteBuf order(ByteOrder endianness) {
        if (endianness == null) {
            throw new NullPointerException("endianness");
        }
        if (endianness == order()) {
            return this;
        }

        SwappedByteBuf swappedBuf = this.swappedBuf;
        if (swappedBuf == null) {
            this.swappedBuf = swappedBuf = new SwappedByteBuf(this);
        }
        return swappedBuf;
    }

    @Override
    public ByteBuf asReadOnly() {
        return new UnreleasableByteBuf(buf.asReadOnly());
    }

    @Override
    public ByteBuf readSlice(int length) {
        return new UnreleasableByteBuf(buf.readSlice(length));
    }

    @Override
    public ByteBuf readRetainedSlice(int length) {
        return new UnreleasableByteBuf(buf.readRetainedSlice(length));
    }
//略

}

观察者模式

我们在使用Netty时,可能经常会进行这样一段的代码编写,通过addListener方法注册监听器,确保端口绑定成功后,回调通知我们的监听器:

serverBootstrap.bind(port).addListener(future -> {
            if (future.isSuccess()) {
                System.out.println("端口[" + port + "]绑定成功!");
            } else {
                System.err.println("端口[" + port + "]绑定失败!");
                bind(serverBootstrap, port + 1);
            }
        });

步入DefaultPromiseaddListener源码之后,可以看到它会通过addListener0完成监听器注册:

@Override
    public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
        checkNotNull(listener, "listener");

        synchronized (this) {
            addListener0(listener);
        }

        if (isDone()) {
            notifyListeners();
        }

        return this;
    }

最终就会通过异步的方式通知我们的监听器。

private void notifyListeners() {
        EventExecutor executor = executor();
        if (executor.inEventLoop()) {
            final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
            final int stackDepth = threadLocals.futureListenerStackDepth();
            if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
                threadLocals.setFutureListenerStackDepth(stackDepth + 1);
                try {
                //通知所有的监听器
                    notifyListenersNow();
                } finally {
                    threadLocals.setFutureListenerStackDepth(stackDepth);
                }
                return;
            }
        }
 		//通知所有的监听器
        safeExecute(executor, new Runnable() {
            @Override
            public void run() {
                notifyListenersNow();
            }
        });
    }

对应的类图如下图所示:

在这里插入图片描述

迭代器模式

迭代器模式不是很常见,我们还是以Netty中的一段示例代码来了解这种涉及模式,代码执行步骤如下:

  1. 创建两个ByteBuf ,分别是headerbody
  2. 将这两个ByteBuf 添加到CompositeByteBuf
  3. 基于CompositeByteBuf 迭代这两个ByteBuf数组。
 public static void main(String[] args) {
		 //创建两个ByteBuf ,分别是header和body
        ByteBuf header = Unpooled.wrappedBuffer(new byte[]{1, 2, 3});
        ByteBuf body = Unpooled.wrappedBuffer(new byte[]{4, 5, 6});

        CompositeByteBuf byteBuf = ByteBufAllocator.DEFAULT.compositeBuffer(2);
		//这两个ByteBuf 添加到CompositeByteBuf`
        byteBuf.addComponent(true, header);
        byteBuf.addComponent(true, body);
        
		//基于CompositeByteBuf 迭代这两个ByteBuf数组
        Iterator<ByteBuf> iterator = byteBuf.iterator();
        while (iterator.hasNext()){
            ByteBuf next = iterator.next();
            int len = next.readableBytes();
            byte[] bytes=new byte[len];

            next.readBytes(bytes);

            for (byte b : bytes) {
                System.out.println(b);
            }
        }
    }

这种模式在java中非常常见,其特点为:

  1. 将集合与迭代职责分离,让集合类专门处理集合添加,聚合一个迭代器负责迭代。
  2. 集合类继承集合接口实现添加删除操作。
  3. 集合类继承迭代器接口获取迭代器获取能力。
  4. 迭代器继承迭代器接口,并以聚合的方式聚合到类中。

首先看看集合类:

  1. 继承Iterable获取迭代器的能力。
  2. 基于addComponent实现集合添加能力。
public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable<ByteBuf> {


private static final Iterator<ByteBuf> EMPTY_ITERATOR = Collections.<ByteBuf>emptyList().iterator();


 @Override
    public Iterator<ByteBuf> iterator() {
        ensureAccessible();
        if (components.isEmpty()) {
            return EMPTY_ITERATOR;
        }
        return new CompositeByteBufIterator();
    }

public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) {
        checkNotNull(buffer, "buffer");
        addComponent0(increaseWriterIndex, components.size(), buffer);
        consolidateIfNeeded();
        return this;
    }
 
 }

查看迭代器:

  1. 继承Iterator实现迭代能力。
  2. 以内部类的方式实现迭代器。
//以内部类的形式继承Iterator实现迭代能力
private final class CompositeByteBufIterator implements Iterator<ByteBuf> {
        private final int size = components.size();
        private int index;

        @Override
        public boolean hasNext() {
            return size > index;
        }

        @Override
        public ByteBuf next() {
            if (size != components.size()) {
                throw new ConcurrentModificationException();
            }
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            try {
                return components.get(index++).buf;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("Read-Only");
        }
    }

责任链模式

责任链模式在Netty中最常见了,例如我们希望读处理器在一条链上不断传播,我们就会通过这样一段代码:

public class InBoundHandlerC extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("InBoundHandlerC: " + msg);
        ctx.fireChannelRead(msg);
    }
}

fireChannelRead就会通过findContextInbound找到下一个处理器继续调用channelRead像一条链条一样传播下去。

 @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }

查看findContextInbound细节可知,这些ChannelInboundHandlerAdapter 都是通过AbstractChannelHandlerContext 封装再进行串联,确保每个消息都会通过这条链传播到所有ChannelInboundHandlerAdapter

private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

对应的类图如下所示:

在这里插入图片描述

参考文献

netty的设计模式:https://blog.csdn.net/fst438060684/article/details/81155203?csdn_share_tail={"type"%3A"blog"%2C"rType"%3A"article"%2C"rId"%3A"81155203"%2C"source"%3A"shark_chili3007"}&fromshare=blogdetail

netty设计模式-单例模式:https://blog.csdn.net/fst438060684/article/details/81156825

迭代器模式:https://www.runoob.com/design-pattern/iterator-pattern.html

设计模型之责任链模式含UML完整实例):https://blog.csdn.net/atu1111/article/details/105558539

一次性搞懂设计模式–迭代器模式:https://zhuanlan.zhihu.com/p/537080924

行为型模式:https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/behavioral.html

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

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

相关文章

探索Linux服务器配置信息的命令

目录 前言1 uname2 lscpu3 free4 df5 lspci6 lsusb7 lshw结语 前言 Linux系统提供了许多命令&#xff0c;用于获取和查看服务器的软硬件配置信息。这些命令可以帮助管理员和用户了解系统的状态、资源使用情况以及硬件设备的相关信息。以下是一些常用的命令以及它们的作用、使用…

【单调栈]LeetCode84: 柱状图中最大的矩形

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的知识点 单调栈 题目 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形…

解决kernel32.dll丢失的修复方式,kernel32.dll预防错误的方法

kernel32.dll文件是电脑中的一个重要文件&#xff0c;如果电脑出现kernel32.dll丢失的错误提示&#xff0c;那么电脑中的一些程序将不能正常使用&#xff0c;那么出现这样的问题有什么解决办法呢&#xff1f;那么今天就和大家说说解决kernel32.dll丢失的修复方式。 一.kernel32…

elasticsearch|大数据|kibana的安装(https+密码)

前言&#xff1a; kibana是比较好安装的&#xff0c;但https密码就比较麻烦一些了&#xff0c;下面将就如何安装一个可在生产使用的kibana做一个简单的讲述 一&#xff0c; kibana版本和下载地址 这里我想还是强调一下&#xff0c;kibana的版本需要和elasticsearch的版本一…

数据库基础(实体,管理系统,日志,数据类型,键与约束)

基本概念 数据&#xff08;Data&#xff09;&#xff1a; 数据是描述事物的信息&#xff0c;可以是数字、文字、图像、音频等形式。数据库中存储的就是这些数据&#xff0c;这些数据可以是具体的实体&#xff08;如一个人的信息&#xff09;&#xff0c;也可以是抽象的概念&…

数据持久化与临时存储的对决:localStorage 与 sessionStorage(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Elasticsearch的 8.x常用api汇总

ES的查询语法比较复杂,对于初学者需要在不断练习中才会逐渐掌握,本文汇总了ES各种查询语法以及常用api,可以作为新手的实用笔记 首先,安装 Kibana! 下载Elasticsearch,官方下载页面;Elasticsearch 参考,官方文档;<

智能优化算法应用:基于静电放电算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于静电放电算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于静电放电算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.静电放电算法4.实验参数设定5.算法结果6.…

目标检测图片截取目标分类图片

如果要训练一个分类模型却没有特定的分类数据集怎么办呢&#xff1f;可以换一种思路&#xff0c;将带有该目标的图片对所有想要的目标进行画标注框然后进行截图&#xff0c;就能得到特定的分类数据了。这么做的目的是&#xff1a;带有该目标的图片可能不会少&#xff0c;但是带…

【系统设计】如何确保消息不会丢失?

一、前言 对于大部分业务系统来说&#xff0c;丢消息意味着数据丢失&#xff0c;是完全无法接受的。其实&#xff0c;现在主流的消息队列产品都提供了非常完善的消息可靠性保证机制&#xff0c;完全可以做到在消息传递过程中&#xff0c;即使发生网络中断或者硬件故障&#xf…

Initial用法-FPGA入门3

Initial是什么 FPGA Initial是一种在FPGA中进行初始化的方法。在FPGA设备上&#xff0c;初始值决定了逻辑门的状态和寄存器的初始值。FPGA Initial可以通过设置初始值来控制电路在上电后的初始状态。 Initial的作用 2.1&#xff0c;控制电路启动时的初始状态 通过设置FPGA Ini…

迅为RK3568开发板使用OpenCV处理图像-ROI区域-位置提取ROI

在图像处理过程中&#xff0c;我们可能会对图像的某一个特定区域感兴趣&#xff0c;该区域被称为感兴趣区域&#xff08;Region of Interest, ROI&#xff09;。在设定感兴趣区域 ROI 后&#xff0c;就可以对该区域进行整体操作。 位置提取 ROI 本小节代码在配套资料“iTOP-3…

KVM虚拟机console使用

注意这些设置都在你要进入虚拟机里设置&#xff0c;不是在你的物理机设置 首先debian12 需要设置 grep ttyS0 /etc/securetty #没有则加上 echo ttyS0 >> /etc/securetty #启动 systemctl start serial-gettyttyS0 systemctl enable serial-gettyttyS0#CentOS Stream …

MIT18.06线性代数 笔记3

文章目录 对称矩阵及正定性复数矩阵和快速傅里叶变换正定矩阵和最小值相似矩阵和若尔当形奇异值分解线性变换及对应矩阵基变换和图像压缩单元检测3复习左右逆和伪逆期末复习 对称矩阵及正定性 特征值是实数特征向量垂直>标准正交 谱定理&#xff0c;主轴定理 为什么对称矩…

智能优化算法应用:基于供需算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于供需算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于供需算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.供需算法4.实验参数设定5.算法结果6.参考文献7.MA…

企业微信旧版-新版网络连接错误,无法登录的解决方案

一.企业微微信无法登录故障 二.解决方案 1.网上的解决方案 **检查网络连接&#xff1a;**确保你的计算机正常连接到互联网。尝试打开其他网页&#xff0c;以确保网络连接正常。 **防火墙和安全软件&#xff1a;**某些防火墙或安全软件可能会阻止企业微信的正常连接。请确保你…

CGAL的3D网格参数化

1、介绍 参数化曲面相当于找到一个从合适的域到曲面的单射映射。一个好的映射是在某种意义上最小化角度失真&#xff08;保角参数化&#xff09;或面积失真&#xff08;等面积参数化&#xff09;的映射。在这个包中&#xff0c;我们专注于参数化与圆盘或球体同胚的三角化曲面&a…

用CC三维建模建出的OSGB格式,用模方打不开,显示该路径包含OSGB瓦块数量0,是什么原因?

答&#xff1a;模方只识别tile命名的模型文件&#xff0c;此模型是不分块输出&#xff0c;要平面切块重新跑。 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件。模方4.1新增自动单体化建模功能&…

Kubernetes 容器编排(2)

可视化部署 官方Dashboard 部署Dashboard # kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml # kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard # 注意将 type: ClusterIP 改为 type: NodePo…

每周一算法:树形动态规划

树形动态规划 树形动态规划一般用于处理求树上最优值的问题。大多数动态规划问题都是在一维二维这种规则的背景下的&#xff0c;可以解决的问题比较局限&#xff0c;而树作为一种特殊的图&#xff0c;可以描述比较复杂的关系&#xff0c;再加上树的递归定义&#xff0c;是一种…