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")@Overridepublic EventExecutorChooser newChooser(EventExecutor[] executors) {//如果executors的长度是2的次幂则返回PowerOfTowEventExecutorChooser,反之返回GenericEventExecutorChooserif (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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic final boolean hasMemoryAddress() {return buf.hasMemoryAddress();}@Overridepublic final long memoryAddress() {return buf.memoryAddress();}@Overridepublic final int capacity() {return buf.capacity();}@Overridepublic ByteBuf capacity(int newCapacity) {buf.capacity(newCapacity);return this;}@Overridepublic final int maxCapacity() {return buf.maxCapacity();}@Overridepublic final ByteBufAllocator alloc() {return buf.alloc();}//使用buf实现,返回当前对象,从而对外屏蔽内部实现细节@Overridepublic ByteBuf retain() {buf.retain();return this;}//略
}

对应的类图如下所示:

在这里插入图片描述

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

final class UnreleasableByteBuf extends WrappedByteBuf {private SwappedByteBuf swappedBuf;UnreleasableByteBuf(ByteBuf buf) {super(buf);}@Overridepublic 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;}@Overridepublic ByteBuf asReadOnly() {return new UnreleasableByteBuf(buf.asReadOnly());}@Overridepublic ByteBuf readSlice(int length) {return new UnreleasableByteBuf(buf.readSlice(length));}@Overridepublic 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完成监听器注册:

@Overridepublic 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() {@Overridepublic void run() {notifyListenersNow();}});}

对应的类图如下图所示:

在这里插入图片描述

迭代器模式

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

  1. 创建两个ByteBuf ,分别是headerbody
  2. 将这两个ByteBuf 添加到CompositeByteBuf
  3. 基于CompositeByteBuf 迭代这两个ByteBuf数组。
 public static void main(String[] args) {//创建两个ByteBuf ,分别是header和bodyByteBuf 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();@Overridepublic 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;@Overridepublic boolean hasNext() {return size > index;}@Overridepublic 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();}}@Overridepublic void remove() {throw new UnsupportedOperationException("Read-Only");}}

责任链模式

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

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

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

 @Overridepublic 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.hqwc.cn/news/278579.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

修改yocto容量和编译

方法1&#xff1a; 1、修改bitbake.conf cd /home/yocto_build/axxia_support/yocto_build/poky/meta/conf/ vi bitbake.conf修改倍数参数&#xff0c;1.3为5G&#xff0c;13为50G IMAGE_OVERHEAD_FACTOR修改这个参数容量&#xff0c;大于initramfs&#xff0c;不然会报错 I…

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

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

Go 与 Rust:现代编程语言的深度对比

在快速发展的软件开发领域中&#xff0c;选择合适的编程语言对项目的成功至关重要。Go 和 Rust 是两种现代编程语言&#xff0c;它们都各自拥有一系列独特的特性和优势。本文旨在深入比较 Go 和 Rust&#xff0c;从不同的角度分析这两种语言&#xff0c;包括性能、语言特性、生…

UG NX二次开发(C++)-库缺少需要的入口点的原因与解决方案

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、“库缺少需要的入口点”错误展示3、可能出现的原因与解决方案3.1 对于采用CTRL+U方式调用3.2 对于menu菜单下调用1、前言 在UG NX二次开发过程中,有时会遇到形形色色的bug,比如有个读…

使用React实现随机颜色选择器,JS如何生成随机颜色

背景 在标签功能中&#xff0c;由于有「背景色」属性&#xff0c;每次新增标签时都为选择哪种颜色犯难。因此&#xff0c;我们思考如何通过JS代码生成随机颜色&#xff0c;提取一个通用的随机颜色生成工具&#xff0c;并基于React框架封装随机颜色选择器组件。 实际效果 原理…

Leaflet.Graticule源码分析以及经纬度汉化展示

目录 前言 一、源码分析 1、类图设计 2、时序调用 3、调用说明 二、经纬度汉化 1、改造前 2、汉化 3、改造效果 总结 前言 在之前的博客基于Leaflet的Webgis经纬网格生成实践中&#xff0c;已经深入介绍了Leaflet.Graticule的实际使用方法和进行了简单的源码分析。认…

插头是什么

插头 电工电气百科 文章目录 插头前言一、插头是什么二、插头的类别三、插头的作用原理总结前言 插头的设计和结构会根据不同的国家和地区的标准和电源类型而有所不同。所以,在使用插头时,需要注意使用符合当地标准和规定的插头,以确保电气安全以及插入正确的电源插座 一、…

新钛云服助力爱达邮轮·魔都号首航,保驾护航,共创辉煌

随着2024年1月1日的临近&#xff0c;中国首艘国产大型邮轮——爱达邮轮魔都号即将迎来激动人心的首航时刻。作为爱达邮轮的IT系统运维和安全服务伙伴&#xff0c;新钛云服有幸提前登船体验&#xff0c;并为魔都号即将到来的航行提供全面的技术支持与保障。 爱达魔都号&#xff…

如何提升数据结构方面的算法能力?

谈及为什么需要花时间学算法&#xff0c;我至少可以列举出三个很好的理由。 (1)性能&#xff1a;选择正确的算法可以显著提升应用程序的速度。仅就搜索来说&#xff0c;用二分查找替 换线性搜索就能为我们帶来巨大的收益。 (2)安全性&#xff1a;如果你选用了错误的算法&…

深入理解 hash 和 history:网页导航的基础(下)

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

spring 笔记七 Spring JdbcTemplate

文章目录 Spring JdbcTemplateJdbcTemplate概述JdbcTemplate开发步骤Spring产生JdbcTemplate对象 Spring JdbcTemplate JdbcTemplate概述 它是spring框架中提供的一个对象&#xff0c;是对原始繁琐的JdbcAPI对象的简单封装。spring框架为我们提供了很多的操作模板类。例如&am…

指纹浏览器有什么用?AdsPower 指纹浏览器都有哪些优势?

说到指纹浏览器&#xff0c;各位跨境卖家肯定都不陌生&#xff0c;指纹浏览器已经成为跨境电商不可或缺的有力工具&#xff0c;那么它具体有什么作用呢&#xff1f;如今市场上指纹浏览器品牌琳琅满目&#xff0c;东哥有没有什么推荐呢&#xff1f;在这里&#xff0c;东哥将为大…