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.mzph.cn/news/226899.shtml

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

相关文章

云开发微信小程序实战

随着移动互联网的快速发展&#xff0c;微信小程序作为一种轻量级的应用程序&#xff0c;逐渐成为了企业开展业务和提升用户体验的重要工具。而云开发则为企业提供了高效、安全、可靠的后台服务&#xff0c;使得小程序的开发和维护更加便捷。本文将详细介绍如何使用微信小程序与…

返回零长度的数组或集合,而不是null

返回零长度的数组或集合而不是 null 是一种良好的编程实践&#xff0c;可以提高代码的可靠性和可读性。以下是一个例子&#xff0c;展示了返回零长度的数组或集合的情况&#xff1a; import java.util.ArrayList; import java.util.List;public class StudentManager {private…

牛客第一期

1.创建动态数组 #include <iostream> using namespace std;int main() {int n; cin>>n; int *pnew int [n]; int i0; for(i0;i<n;i) {*(pi)ni; } int j0; for(j0;j<n;j) {printf("%d ",*(pj)); } } #include<bits/stdc.h> using namespace s…

网站提示“不安全”

当你在浏览网站时&#xff0c;有时可能会遇到浏览器提示网站不安全的情况。这通常是由于网站缺乏SSL证书所致。那么&#xff0c;从SSL证书的角度出发&#xff0c;我们应该如何解决这个问题呢&#xff1f; 首先&#xff0c;让我们简单了解一下SSL证书。SSL证书是一种用于保护网站…

python实战教学之python版“张万森,好久不见”

前言 WINTER IS COMING 最近《一闪一闪亮星星》的电影在火热预售中&#xff0c;家人们抢到票了嘛&#xff0c;前两天小编写了一篇“张万森&#xff0c;下雪了”的文章后&#xff0c;收到了不少小伙伴的反馈&#xff1a;“代码的运行结果只有文字&#xff0c;没有雪花啊”&#…

OceanBase数据库部署

文章目录 OceanBase基础概念集群、Zone和OB ServerRootService总控服务&#xff08;RS&#xff09;多租户机制&#xff1a;资源隔离&#xff0c;数据隔离每个租户拥有若干资源池&#xff08;Resource Pool&#xff09; 部署形式部署流程OceanBase客户端工具 学习体验部署实现 O…

VSCode配置代码片段,提升效率必备!

1.点击文件—> 首选项------>配置用户代码片段 2、新建用户代码片段 3、以js的控制台输出为例 {//片段名称"console.log": {"prefix": "cls",//呼出命令"body": ["console.log($1)"//具体片段],"descriptio…

netty-daxin-3(rpc远程调用)

文章目录 nettyRpcObjectEncoder 与 ObjectDecoderjdk动态代理回顾Rpc调用过程简析服务端客户端 nettyRpc ObjectEncoder 与 ObjectDecoder ObjectEncoder继承自MessageToByteEncoder<Serializable>&#xff0c;它内部使用ByteBufOutputStream包装ByteBuf对象&#xff…

vue3项目中使用vue-cropper实现截图效果

前言&#xff1a; 自用&#xff01;&#xff01;&#xff01; 插件文档链接&#xff1a; https://www.npmjs.com/package/vue-cropper 使用步骤&#xff1a; 1、安装 npm install vue-cropper 2、项目中引入&#xff08;全局引入&#xff09;&#xff1b;文档中有介绍如何局…

怎么解决bash: composer: command not found问题

是不是遇到过bash: composer: command not found问题&#xff0c;怎么解决呢&#xff1f;下面由composer教程栏目给大家来详细介绍该问题的解决方法。 1、先看报错 2、由于错误的原因&#xff0c;安装很多东西都失败了。网上有的说是环境变量的问题&#xff0c;又一个个找也没…

DS考研真题总结——客观题(1)

开始整理真题中的客观小题&#xff0c;至于和算法有关的大题统一最后整理~ 定义背诵&#xff1a;数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效…

spring面试:二、bean的生命周期和循环引入问题(三级缓存、@Lazy)

bean的生命周期 Spring容器在进行实例化时&#xff0c;会将xml配置的的信息封装成一个BeanDefinition对象&#xff0c;Spring根据BeanDefinition来创建Bean对象&#xff0c;里面有很多的属性用来描述Bean。 其中比较重要的是&#xff1a; beanClassName&#xff1a;bean 的类…

tcp连接全过程各种状态详解

文章目录 TCP的一些重要特性tcp连接全过程各种状态参考资料 TCP的一些重要特性 TCP是一种可靠、面向连接、全双工、流控制、拥塞控制、有序传输、无差错传输、无重复传输、无丢失传输等特点的协议。为了实现这些特点&#xff0c;TCP必须对上层应用程序发送的数据进行分段、重组…

spring6 为集合类型属性赋值 --引用集合类型的bean

1.准备工作&#xff1a; Student.java package bean.dimap;import java.util.List; import java.util.Map;public class Student {private String sid;private String sname; //private Map<String,Teacher> map;//private List<Lesson> lessonList;public List&…

内网渗透、哈希传递、获取域控权限

网络拓扑 kali&#xff08;桥接&#xff09; 10.9.47.221 win11&#xff08;nat 模式&#xff09; 192.168.1.130 DMZ&#xff08;win7&#xff09; 网卡1&#xff08;桥接&#xff09;10.9.47.110 网卡2&#xff08;仅主机&#xff09;172.16.1.128 域控&#xff08;win 20…

算法模板之单链表图文讲解

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;算法模板、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️使用数组模拟单链表讲解1.1 &#x1f514;为什么我们要使用数组去模拟单链表…

【TB作品】基于单片机的实验室管理系统,STM32,GM65二维码扫描模块

硬件&#xff1a; &#xff08;1&#xff09;STM32F103C8T6最小板&#xff08;&#xff09; &#xff08;2&#xff09;GM65二维码扫描模块 &#xff08;3&#xff09;DS1302实时时钟模块 &#xff08;4&#xff09;AT24C02 存储设备 &#xff08;5&#xff09;蜂鸣器 &#xf…

同义词替换降低论文重复率的最新研究成果 神码ai

大家好&#xff0c;今天来聊聊同义词替换降低论文重复率的最新研究成果&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;同义词替换降低论文重复率的最新研…

Element-Ui定制Dropdown组件

1.效果 说明&#xff1a;移入后新增图标&#xff0c;然后移入后图标变色。当然大家可以想到用mouseover移入事件来实现移入颜色的变化&#xff0c;但是在使用Dropdown组件的时候&#xff0c;不支持这种写法。因此采用了原生的遍历对象的形式&#xff0c;为每一个item对象绑定鼠…

拆东墙补西墙-中国金融风控多头借贷数据分析

引言 在中国的金融行业中&#xff0c;多头借贷是一个备受关注的话题。多头借贷是指一个借款人同时从多个金融机构借款&#xff0c;这种行为可能会增加借款人的还款压力&#xff0c;也会增加金融机构的风险。因此&#xff0c;对多头借贷的风险进行有效的监控和分析对于金融机构…