Netty常见的设计模式

简介

设计模式在软件开发中起着至关重要的作用,它们是解决常见问题的经过验证的解决方案。而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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TS系列-keyof的妙用

案例1 1、如果&#xff0c;有一个接口&#xff0c;某个变量的类型&#xff0c;是这个接口的 key &#xff1f; keyof 后面可以跟 一个对象类型或者一个接口类型keyof 是把后面 对象或者接口 的 键 都提取出来&#xff0c;组成一个联合类型 interface IStudentAttr {name: stri…

【LeetCode刷题笔记(6-1)】【Python】【三数之和】【哈希表】【中等】

文章目录 三数之和题目描述示例示例1示例2示例3 提示解决方案1&#xff1a;【三层遍历查找】解决方案2&#xff1a;【哈希表】【两层遍历】 结束语 三数之和 三数之和 题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! …

nodejs微信小程序+python+PHP血液中心管理平台的设计与实现-计算机毕业设计推荐

在二十一世纪的今天&#xff0c;我国献血总量已经不容小觑&#xff0c;在全国人民的不懈努力下&#xff0c;贫血、缺血的病人已经有了足够的血液保障。与此同时&#xff0c;采血工作和血液入库、出库等工作也日愈繁重。为进一步提高采血工作和血液中心的工作效率&#xff0c;开…

【算法与数据结构】376、LeetCode摆动序列

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题难点在于要考虑到不同序列的情况&#xff0c;具体来说要考虑一下几种特殊情况&#xff1a; 1、上…

提前预警,时刻守护:迅软DLP的数据安全先锋

许多数据泄密事件的发生&#xff0c;往往都是由于没有在案发事前做好安全保护&#xff0c;使得重要信息被随意攻击、盗取、泄密。比起在危机发生后亡羊补牢&#xff0c;更重要的是应该在案发之前未雨绸缪。迅软DLP作为迅软股份研发的“重磅选手”&#xff0c;可为政企单位在一切…

物联网智能仓库解决方案

物联网智能仓库解决方案是一种基于物联网技术的仓库管理系统&#xff0c;通过自动化设备、智能化管理系统和大数据分析等技术&#xff0c;实现仓库的智能化运营和管理。 物联网智能仓库解决方案包括&#xff1a; 仓库设备自动化&#xff1a;通过自动化设备和技术&#xff0c;实…

OpenHarmony关于修改系统横屏导致启动视频显示不全问题解决

前言 OpenHarmony源码版本&#xff1a;4.0release 开发板&#xff1a;DAYU / rk3568 前段时间写的设置OpenHarmony启动视频&#xff0c;在竖屏状态下是正常的&#xff0c;但是横屏状态下显示不全。 链接直达&#xff1a;OpenHarmony 设备启动Logo和启动视频替换指南-CSDN博…

docker小白第四天

docker小白第一天 什么是镜像 1、是一种轻量级、可执行的独立软件包&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等)&#xff0c;这个打包好的运行环境就…

基于轻量级yolov5-seg全系列【n/s/m/l/x】参数模型开发构建工业场景下不同参数量级的滚珠丝杠传动表面缺陷分割检测系统

工业场景下的滚珠丝杠传动表面缺陷分割检测系统在我们前面的博文中已经有了相关的开发实践了&#xff0c;感兴趣的话可以自行阅读即可&#xff1a; 《助力工业生产质检&#xff0c;基于轻量级yolov5-seg开发构建工业场景下滚珠丝杠传动表面缺陷分割检测系统》 前文主要是以se…

【PS】修改 图片 文字

删除文字 1&#xff1a;框选要修改的文字 选择-色彩范围 调整色彩容差能看见字体的时候就OK&#xff08;记住用吸管吸取文字颜色&#xff09; 2&#xff1a;选择-修改-扩展-像素2 3&#xff1a;编辑-内容识别填充 现在文字去除了。 用污点画笔修复工具&#xff0c;对缺陷进行…

四十七、Redis分片集群

目录 一、分片集群结构 二、散列插槽 1、Redis如何判断某个key应该在哪个实例&#xff1f; 2、如何将同一类数据固定的保存在同一个Redis实例&#xff1f; 三、集群伸缩 四、故障转移 1、当集群中有一个master宕机时 &#xff08;1&#xff09;自动转移 &#xff08;2&…

[渗透测试学习] Codify - HackTheBox

首先nmap扫描端口 nmap -sV -sC -p- -v --min-rate 1000 10.10.11.239扫出来三个端口&#xff0c;22端口为ssh服务&#xff0c;80端口有http服务&#xff0c;3000端口为nodejs框架 尝试访问下80端口&#xff0c;发现页面重定向 将该域名添加到hosts里 sudo vim /etc/hosts 成…

MySQL数据库的基础概念

目录 顾名思义&#xff0c;数据库是用于存储数据的&#xff0c;那这些数据被存储在哪呢&#xff1f; 文件也能存储数据&#xff0c;那在这个基础上&#xff0c;为什么还要搞出一个数据库来存储数据呢&#xff1f; MySQL的客户端登录/退出指令、服务端的启动/关闭指令 数据…

mysql数据库损坏后重装,数据库备份

重装 先卸载 sudo apt-get remove --purge mysql-server mysql-client mysql-common sudo apt-get autoremove sudo apt-get autoclean 然后重新安装MySQL: sudo apt-get install mysql-server mysql-client 首先要先使用无密码登录数据库一定要使用 sudo mysql -uroo…

数据结构(七):树介绍及面试常考算法

一、树介绍 1、定义 树形结构是一种层级式的数据结构&#xff0c;由顶点&#xff08;节点&#xff09;和连接它们的边组成。 树类似于图&#xff0c;但区分树和图的重要特征是树中不存在环路。树有以下特点&#xff1a; &#xff08;1&#xff09;每个节点有零个或多个子节点…

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

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

computed 和 watch 的奇妙世界:让数据驱动你的 Vue 应用(下)

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

idea2023解决右键没有Servlet的问题

复制Servlet Class.java中的文件。 回到文件&#xff0c;然后点击小加号 然后输入刚刚复制的东西&#xff1a; 3. 此时右键有servlet。 4. 然后他让你输入下面两个框&#xff1a; JAVAEE TYPE中输入Servlet Class Name 表示你要创建的Servlet类的名称是什么。自己起名字。然后…

寒冷冬天,再次撕下了新能源汽车的续航遮羞布,北方真不适合

随着懂车帝的冬测&#xff0c;新能源汽车的冬天续航成为关注焦点&#xff0c;电池性能在冬天里缩减众所周知&#xff0c;不过从来没有机构告诉消费者&#xff0c;到底冬天电池的续航会减少多少&#xff0c;如今这一切显然暴露在人们眼前了。 懂车帝的冬测显示除了特别优秀的新能…

Elasticsearch的 8.x常用api汇总

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