Netty源码分析之设计模式在Netty框架中的应用

如果你对设计模式不是很熟悉,建议补习一下设计模式再来阅读本章的内容。本章主要分析设计模式的应用案例,不对设计模式本身做详细讲解。

1.1 单例模式源码举例

单例模式要点回顾如下。

(1)一个类在任何情况下只有一个对象,并提供一个全局访问点。

(2)延迟创建。

(3)避免线程安全问题。

Netty中的单例模式应用非常广泛,单例模式本身也比较简单,下面列举MqttEncoder类,采用饿汉式单例的写法。饿汉式单例最大的优点就是简单,绝对线程安全。具体代码如下。

1.2 策略模式源码举例

策略模式要点回顾如下。

(1)封装一系列可相互替换的算法家族。

(2)动态选择某一个策略。

Netty在根据CPU核数分配线程数量的一个优化时,如果是2的平方则采用PowerOfTwoEventExecutorChooser来创建EventExecutorChooser,如果不是2的平方则采用GenericEventExecutorChooser来创建EventExecutorChooser,这里用的是三元运算选择策略,具体代码如下。

1.3 装饰者模式源码举例

装饰者模式要点回顾如下。

(1)装饰者和被装饰者实现同一个接口。

(2)装饰者通常继承被装饰者,同宗同源。

(3)动态修改、重载被装饰者的方法。

从Netty的ByteBuf类结构图可以看到,ByteBuf的直接实现类有五个,忽略WrappedByteBuf这个类,其实直接实现类有四个。为什么要忽略掉WrappedByteBuf呢?因为它是ByteBuf装饰者的基类,本身没有任何实现功能。来看WrappedByteBuf的代码,主要功能就是保存被装饰者的引用。

具体的装饰者,继承上面的装饰者的顶级类,在自己的构造函数中接收ByteBuf的类型的参数,并把它传递给它的父类,用户在调用装饰者时,会把创建的最上面的四种待装饰的组件类以构造方法的形式传递进去,整个体系就运行起来了。而且装饰者可以按照自己的需求重写父类的方法,或者在现在的基础上添加新的方法调用进行增强。例如UnreleasableByteBuf类,重写了release()方法,并返回false表示UnreleasableByteBuf不支持被释放。

再比如SimpLeakAwareByteBuf类,从字面意思来看,其实是一个内存泄露感知的ByteBuf,同样继承自WrappedByteBuf。从构造方法来看,其构造参数多了一个ResourceLeak,主要用于对内存泄露的跟踪。主要差异在于增加了release()方法,代码如下。

看到上面的代码,还是来关注release()方法。它调用release()方法,根据返回结果额外增加了一个资源泄露的监控行为。

1.4 观察者模式源码举例

观察者模式要点回顾如下。

(1)两个角色:观察者和被观察者。

(2)观察者订阅消息,被观察者发布消息。

(3)订阅则能收到消息,取消订阅则收不到消息。

Netty里面的观察者和被观察者模式一般用Promise和Future来实现。项目中用得比较多的一个方法就是channel.writeAndFlush()方法。当调用channel.writeAndFlush()方法的时候,实际上就是创建了一个被观察者ChannelFuture,来看源码。

writeAndFlush()方法的返回值是ChannelFuture。当调用ChannelFuture的addListener()方法的时候,其实就是往ChannelFuture中添加被一个ChannelPromise,继续往下跟踪源码。

上面的writeAndFlush()方法还有一个重载方法,其中一个参数就是ChannelPromise,通常情况下Future和Promise是成对出现的。我们发现ChannelPromise就是ChannelFuture的子类,在Promise中定义了非常多的回调方法,提供给用户去重载,用户用自己的逻辑通常实现各种Listener接口来重载达到回调通知的目的。

1.5 迭代器模式源码举例

迭代器模式要点回顾如下。

(1)实现迭代器接口。

(2)实现对容器中的各个对象逐个访问的方法。

Netty里面的CompositeByteBuf这个零拷贝的实现,就使用了迭代器模式。首先看一段代码。

这段代码把两个ByteBuf添加到一起,forEachByte就是实现了迭代器模式。那么为什么说它是零拷贝呢?

我们找到forEachByte()的实现,在AbstractByteBuf里面,有下面这样一段代码。

从readerIndex开始读,读到writeIndex。继续跟进forEachByteAsc0()方法,查看源码。

继续看getByte()方法的实现,找到CompositeByteBuf类的实现。

先找到Index对应的Component,然后迭代的时候直接返回Component的Byte内容,就实现了零拷贝。其他的ByteBuf如果迭代的话,可能会把所有的数据都拷贝一遍。

1.6 责任链模式源码举例

责任链是指多个对象都有机会处理同一个请求,从而避免请求的发送者和接收者之间的耦合关系。然后,将这些对象连成一条链,并且沿着这条链往下传递请求,直到有一个对象可以处理它为止。在处理过程中,每个对象只处理它自己关心的那一部分,不相关的部分可以继续往下传递,直到链中的某个对象不想处理,可以将请求终止或丢弃。

责任链模式要点回顾如下。

(1)需要有一个顶层责任处理接口。

(2)需要有动态创建链、添加和删除责任处理器的接口。

(3)需要有上下文机制。

(4)需要有责任终止机制。

Netty的Pipeline就是采用了责任链设计模式,底层采用双向链表的数据结构,将链上的各个处理器串联起来。客户端每一个请求到来,Netty都认为是Pipeline中所有的处理器都有机会处理它。因此,对于入栈的请求,全部从头节点开始往后传播,一直传播到尾节点(来到尾节点的msg才会被释放)。

Netty的责任链模式中的组件如下。

(1)责任处理器接口ChannelHandler。

(2)添加删除责任处理器的接口ChannelPipeline。

(3)上下文组件ChannelHandlerContext,可获得用户记需要的数据。

(4)终止责任链的ctx.fireXXX()方法,可终止传播事件。

Pipeline中所有的Handler为顶级抽象接口,它规定了所有的Handler统一要有添加、移除、异常捕获的行为。

Pipeline中的Handler被封装进了上下文中。通过上下文可以轻松获取当前节点所属的Channel及其线程执行器。

责任终止机制包括如下内容。

(1)Pipeline中的任意一个节点,只要不手动往下传播,这个事件就会在当前节点终止传播。

(2)对于入栈数据,默认会传递到尾节点进行回收,如果不进行下一步传播,事件就会终止在当前节点。

(3)对于出栈数据,头节点使用Unsafe对象,把数据写回客户端也意味着事件的终止。

底层事件的传播使用的就是针对链表的操作,如AbstractChannelHandlerContext类的findContextInbound()方法。

1.7 工厂模式源码举例

工厂模式的要点就是将创建对象的逻辑封装起来。

我们最先接触ReflectiveChannelFactory就是专门用来创建Channel的工厂,它接收一个Class对象,然后调用Class的newInstance()方法,将创建好的对象返回去。

Netty中的设计模式应用非常多,这里不再一一列举,希望大家花时间去探索,这样才能够有更多的收获。本章旨在为大家打开深入探索Netty的大门,有不妥之处请纠正交流。

展开阅读全文

页面更新:2024-06-02

标签:源码   模式   观察者   节点   要点   框架   接口   对象   代码   方法   责任

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top