当前位置: 首页 > news >正文

【Netty】netty中都是用了哪些设计模式

对于工程师来说,掌握并理解运用设计模式,是非常重要的,但是除了学习基本的概念之外,需要结合优秀的中间件、框架源码学习其中的优秀软件设计,这样才能以不变应万变。

单例模式

单例模式解决的对象的唯一性,一般来说就是构造方法私有化、然后提供一个静态的方法获取实例。
在netty中,select用于处理CONTINUE、SELECT、BUSY_WAIT 三种策略,通过DefaultSelectStrategy实现饿汉式。

final class DefaultSelectStrategy implements SelectStrategy {static final SelectStrategy INSTANCE = new DefaultSelectStrategy();private DefaultSelectStrategy() { }@Overridepublic int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {// 是否有任务 ,有就不阻塞  ,否则就阻塞return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;}
}

工厂方法模式

工厂方法解决的是批量同类型的对象的创建过程。
对于netty来说,在客户端和服务端启动过程中,会设置对应的channel类型,如果直接写死,必然不够优雅,所以可以反射方式,通过Class对象,进行不同类的实例化。具体操作就是如下两个方法,先获取Class的构造器,然后在进行newStance实例化。

虽然反射有一定的性能损失,但是这种方式可以有效减少工厂类的数量。所以损失一定的性能,还是非常值得的,但是如果对于性能比较敏感的业务场景,就需要具体问题具体思考了。也就是trade-off,你看不仅仅在分布式系统设计过程中有trade-off,在软件设计,编码层面也需要多思考具体的业务情况。

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {private final Constructor<? extends T> constructor;public ReflectiveChannelFactory(Class<? extends T> clazz) {ObjectUtil.checkNotNull(clazz, "clazz");try {// 通过Class对象 获取构造参数// 等待后续的利用反射进行创建对象this.constructor = clazz.getConstructor();} catch (NoSuchMethodException e) {throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +" does not have a public non-arg constructor", e);}}@Overridepublic T newChannel() {try {// 对象实例化return constructor.newInstance();} catch (Throwable t) {throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);}}@Overridepublic String toString() {return StringUtil.simpleClassName(ReflectiveChannelFactory.class) +'(' + StringUtil.simpleClassName(constructor.getDeclaringClass()) + ".class)";}
}

责任链模式

责任链在netty中就非常熟悉了,那就是通过双向链表构建的channelPipeline,具体

    protected DefaultChannelPipeline(Channel channel) {tail = new TailContext(this); // 构建尾部节点head = new HeadContext(this); // 构建头节点head.next = tail; // 首尾相连接tail.prev = head;}

在这里插入图片描述

这里可以看到将添加到handler封装成了一个HandlerContext,为什么要这么做?

ChannelHandlerContext提供了时间处理的时间传播机制,如果在handler中,那么就需要在所有的用户自定义handler中都是实现,显然成本是比较高的。这种方式就只需要实现一次,直接复用就可以。

    private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);}

建造者模式

构建者模式解决的构建一个复杂对象的过程,而netty服务端和客户端的BootStrap就是这个过程。
比较优雅的是,通过链式编程 可以非常随意的设置对应的配置。

        EventLoopGroup bossGrpup = new NioEventLoopGroup(1);EventLoopGroup wrokerGrpup = new NioEventLoopGroup();ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGrpup,wrokerGrpup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LoggingHandler());}});ChannelFuture f = bootstrap.bind(8888).sync();f.channel().closeFuture().sync();

策略模式

策略模式解决的是针对同一个问题 不同算法的处理,策略之间可以互相替换,比如通过手机号 或者 邮箱可以查询用户的个人信息,就需要根据入参的不同 动态选择不同的策略。当有新增的不同的策略时,因为将稳定层进行了封装起来,只需要调整变化层,对于核心代码的修改很少,也容易减少bug。

netty在选择线程的时候,根据当前的是否是2的次幂,选择不同的策略方式,如果是的2的次幂,选择使用 &,否则选择 % ,&的性能相对来说比% 更高。

public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();private DefaultEventExecutorChooserFactory() { }@Overridepublic EventExecutorChooser newChooser(EventExecutor[] executors) {if (isPowerOfTwo(executors.length)) {return new PowerOfTwoEventExecutorChooser(executors);} else {return new GenericEventExecutorChooser(executors);}}private static boolean isPowerOfTwo(int val) {return (val & -val) == val;}private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {private final AtomicInteger idx = new AtomicInteger();private final EventExecutor[] executors;PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}@Overridepublic EventExecutor next() {return executors[idx.getAndIncrement() & executors.length - 1];}}private static final class GenericEventExecutorChooser implements EventExecutorChooser {private final AtomicLong idx = new AtomicLong();private final EventExecutor[] executors;GenericEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}@Overridepublic EventExecutor next() {return executors[(int) Math.abs(idx.getAndIncrement() % executors.length)];}}
}

装饰者模式

装饰者模式的目的解决的为目标类增加功能,比如原来男人,但是可以增加男人会化妆的功能,
在netty中就是实现了对ByteBuf侧重于在原来功能的拓展,增加日志,事务处理等。而装饰者侧重于为目标类增加新的功能。

代理模式和装饰者模式的区别,前者针对目标类是拓展功能

final class UnreleasableByteBuf extends WrappedByteBuf {private SwappedByteBuf swappedBuf;UnreleasableByteBuf(ByteBuf buf) {super(buf instanceof UnreleasableByteBuf ? buf.unwrap() : buf);}

模板方法模式

模板解决的是拓展问题,通过将一个稳定点,抽象到核心流程,然后交给不同的子类来实现。对于这个实现,在Spring框架中提供了Bean生命周期的拓展点,以实现不同的逻辑。

而在netty中,在初始化的时候,因为客户端和服务端都继承同一个父类,那么将init就可以不同实现。
在这里插入图片描述

    abstract void init(Channel channel) throws Exception;

在这里插入图片描述

在这里插入图片描述

总结

设计模式的学习,不仅仅在于了解各个模式后的使用,而要了解设计背后的目的解决的问题,然后在遇到实际问题的时候,思考能不能使用对应的模式解决。而不是蛮干,炫技使用。另外掌握设计模式最有效的方式就是看各种优秀的中间件、框架源码,netty是一个很好的资源,Spring、MyBatis、JDK等都可以去翻看。

当然看源码的时候,一定要带着目的去读,不然就可能迷失在浩瀚的代码细节中。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 端口安全老化细节
  • 软件测试之UI自动化测试
  • 被审稿人批得体无完肤?参考文献这样引用就对了!
  • 抠像拍照技术在展厅设计中的应用,实现了哪些新颖的互动体验?
  • uniapp vite3 require导入commonJS 的js文件方法
  • mysql可重复读不能解决幻读吗?
  • 安宝特科技 | AR眼镜在安保与安防领域的创新应用及前景
  • 实时地图+瞬移,黑神话地图工具来了
  • LLM模型:代码讲解Transformer运行原理
  • 快速排序的深入优化探讨
  • RedisCache存入redis的数据key为何name和id的分隔符是两个冒号::
  • 2024年高教社杯全国大学生数学建模竞赛A题思路(2024数学建模国赛A题思路)
  • 【Effective Java】多构造器参数使用构建器 (快速上手)
  • 【HuggingFace Transformers】OpenAIGPTModel源码解析
  • MySQL学习--加强
  • 0x05 Python数据分析,Anaconda八斩刀
  • 2017前端实习生面试总结
  • Apache的80端口被占用以及访问时报错403
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • Java到底能干嘛?
  • java第三方包学习之lombok
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Python进阶细节
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • Vue学习第二天
  • webpack入门学习手记(二)
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 欢迎参加第二届中国游戏开发者大会
  • 解析 Webpack中import、require、按需加载的执行过程
  • 模型微调
  • 前端知识点整理(待续)
  • 阿里云ACE认证学习知识点梳理
  • 第二十章:异步和文件I/O.(二十三)
  • ## 基础知识
  • #include<初见C语言之指针(5)>
  • (02)vite环境变量配置
  • (06)金属布线——为半导体注入生命的连接
  • (1)(1.11) SiK Radio v2(一)
  • (7)摄像机和云台
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (四)软件性能测试
  • (一)SvelteKit教程:hello world
  • (转)【Hibernate总结系列】使用举例
  • (转)scrum常见工具列表
  • (转)setTimeout 和 setInterval 的区别
  • (转)负载均衡,回话保持,cookie
  • (转载)PyTorch代码规范最佳实践和样式指南
  • ./configure、make、make install 命令
  • .NET 中的轻量级线程安全
  • .Net6 Api Swagger配置
  • .Net通用分页类(存储过程分页版,可以选择页码的显示样式,且有中英选择)