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

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

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

单例模式

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

final class DefaultSelectStrategy implements SelectStrategy {
    static final SelectStrategy INSTANCE = new DefaultSelectStrategy();

    private DefaultSelectStrategy() { }

    @Override
    public 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);
        }
    }

    @Override
    public T newChannel() {
        try {
            // 对象实例化
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }

    @Override
    public 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>() {
                    @Override
                    protected 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() { }

    @Override
    public 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;
        }

        @Override
        public 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;
        }

        @Override
        public 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等都可以去翻看。

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


http://www.kler.cn/a/294236.html

相关文章:

  • opc da 服务器数据 转 IEC61850项目案例
  • Vue计算属性computed
  • 【PowerHarmony】电鸿蒙学习记录-编写helloworld!
  • 微服务day08
  • 开源项目推荐——OpenDroneMap无人机影像数据处理
  • 阿里云和七牛云对象存储区别和实现
  • BIO、NIO、AIO 有什么区别?
  • 进程间通信-进程池
  • 《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 09部署OSPF
  • 前向代理和反向代理的区别是什么?
  • JWT详解:一种轻量级的身份验证和授权机制
  • 2024年AI芯片峰会——边缘端侧AI芯片专场
  • 力扣172.阶乘后的0
  • elasticsearch文档Delete By Query API(一)
  • 蚂蚁数科独立后首度公布业务进展和战略布局
  • 已经30岁了,想转行从头开始现实吗?什么样的工作算好工作?
  • 网页时装购物系统:Spring Boot框架的高效实现
  • 《Foundation 滑块》
  • Codeforces Round 971 (Div. 4) A~G2
  • 【网络安全】CSRF漏洞—CSRF基础漏洞防御
  • linux系统中,计算两个文件的相对路径
  • class 6: vue.js 3 组件化开发
  • SpringBoot学习(4)(yml配置信息书写和获取)(SpringEL表达式语言)
  • 零工市场小程序:自由职业者的日常工具
  • HarmonyOS开发实战( Beta5版)延迟加载lazy-import实践使用指导
  • 探索EasyCVR与AI技术深度融合:视频汇聚平台的新增长点