本文主要是介绍【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等都可以去翻看。
当然看源码的时候,一定要带着目的去读,不然就可能迷失在浩瀚的代码细节中。
这篇关于【Netty】netty中都是用了哪些设计模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!