【Netty权威指南】16-Netty架构剖析

1、Netty逻辑架构

Netty采用了典型的三层网络架构进行设计和开发,逻辑架构如图20-1所示。

1.1、Reactor通信调度层

它由一系列辅助类完成,包括 Reactor线程 NioEventLoop及其父类, NioSocketChannel、NioServerSocketChannel及其父类, ByteBuffer以及由其衍生出来的各种 Buffer, Unsafe以及其衍生出的各种内部类等。该层的主要职责就是监听网络的读写和连接操作,负责将网络层的数据读取到内存缓冲区中,然后触发各种网络事件,例如连接创建、连接激活读事件、写事件等,将这些事件触发到 Pipeline中,由 Pipeline管理的职责链来进行后续的处理。

1.2、职责链 ChannelPipeline

它负责事件在职责链中的有序传播,同时负责动态地编排职责链。职责链可以选择监听和处理自己关心的事件,它可以拦截处理和向后/向前传播事件。不同应用的 Handler节点的功能也不同,通常情况下,往往会开发编解码 Hanlder用于消息的编解码,它可以将外部的协议消息转换成内部的POJO对象,这样上层业务则只需要关心处理业务逻辑即可,不需要感知底层的协议差异和线程模型差异,实现了架构层面的分层隔离。

1.3、业务逻辑编排层(Service ChannelHandler)

业务逻辑编排层通常有两类:一类是纯粹的业务逻辑编排,还有一类是其他的应用层协议插件,用于特定协议相关的会话和链路管理。例如CMPP协议,用于管理和中国移动短信系统的对接。
架构的不同层面,需要关心和处理的对象都不同,通常情况下,对于业务开发者,只需要关心职责链的拦截和业务 Handler的编排。因为应用层协议栈往往是开发一次,到处运行,所以实际上对于业务开发者来说,只需要关心服务层的业务逻辑开发即可。各种应用协议以插件的形式提供,只有协议开发人员需要关注协议插件,对于其他业务开发人员来说,只需关心业务逻辑定制。这种分层的架构设计理念实现了NIO框架各层之间的解耦,便于上层业务协议栈的开发和业务逻辑的定制正是由于 Netty的分层架构设计非常合理,基于Nety的各种应用服务器和协议栈开发才能够如雨后春笋般得到快速发展。

2、关键架构质量属性

2.1、高性能

影响最终产品的性能因素非常多,其中软件因素如下。
◎架构不合理导致的性能问题。
◎编码实现不合理导致的性能问题,例如锁的不恰当使用导致性能瓶颈。
硬件因素如下。
◎服务器硬件配置太低导致的性能问题。
◎带宽、磁盘的IOPS等限制导致的IO操作性能差
◎测试环境被共用导致被测试的软件产品受到影响
尽管影响产品性能的因素非常多,但是架构的性能模型合理与否对性能的影响非常大。如果一个产品的架构设计得不好,无论开发如何努力,都很难开发出一个高性能、高可用的软件产品。

“性能是设计出来的,而不是测试出来的”。下面我们看Netty的架构设计是如何实现高性能的。
(1)采用异步非阻塞的IO类库,基于 Reactor模式实现,解决了传统同步阻塞IO模式下一个服务端无法平滑地处理线性增长的客户端的问题。
(2)TCP接收和发送缓冲区使用直接内存代替堆内存,避免了内存复制,提升了IO读取和写入的性能。
(3)支持通过内存池的方式循环利用 ByteBuf,避免了频繁创建和销毁 ByteBuf带来的性能损耗。
(4)可配置的I/O线程数、TCP参数等,为不同的用户场景提供定制化的调优参数,满足不同的性能场景。
(5)采用环形数组缓冲区实现无锁化并发编程,代替传统的线程安全容器或者锁。
(6)合理地使用线程安全容器、原子类等,提升系统的并发处理能力。
(7)关键资源的处理使用单线程串行化的方式,避免多线程并发访问带来的锁竞争和额外的CPU资源消耗问题。
(8)通过引用计数器及时地申请释放不再被引用的对象,细粒度的内存管理降低了GC的频率,减少了频繁GC带来的时延增大和CPU损耗
无论是 Netty的官方性能测试数据,还是携带业务实际场景的性能测试,Netty在各个NIO框架中综合性能是最高的。

2.2、可靠性

作为一个高性能的异步通信框架,架构的可靠性是大家选择的一个重要依据。下面我们探讨Netty架构的可靠性设计。

2.2.1、链路有效性检测

由于长连接不需要每次发送消息都创建链路,也不需要在消息交互完成时关闭链路,因此相对于短连接性能更高。对于长连接,一旦链路建立成功便一直维系双方之间的链路,直到系统退出。
为了保证长连接的链路有效性,往往需要通过心跳机制周期性地进行链路检测。使用周期性心跳的原因是:在系统空闲时,例如凌晨,往往没有业务消息。如果此时链路被防火墙Hang住,或者遭遇网络闪断、网络单通等,通信双方无法识别岀这类链路异常。等到第二天业务高峰期到来时,瞬间的海量业务冲击会导致消息积压无法发送给对方,由于链路的重建需要时间,这期间业务会大量失败(集群或者分布式组网情况会好一些)。为了解决这个问题,需要周期性的心跳对链路进行有效性检测,一旦发生问题,可以及时关闭链路,重建TCP连接当有业务消息时,无须心跳检测,可以由业务消息进行链路可用性检测。所以心跳消息往往是在链路空闲时发送的。
为了支持心跳,Netty提供了如下两种链路空闲检测机制。
◎读空闲超时机制:当连续周期T没有消息可读时,触发超时 Handler,用户可以基于读空闲超时发送心跳消息,进行链路检测;如果连续N个周期仍然没有读取到心跳消息,可以主动关闭链路。
◎写空闲超时机制:当连续周期T没有消息要发送时,触发超时 Handler,用户可以基于写空闲超时发送心跳消息,进行链路检测;如果连续N个周期仍然没有接收到对方的心跳消息,可以主动关闭链路。
为了满足不同用户场景的心跳定制, Netty提供了空闲状态检测事件通知机制,用户可以订阅空闲超时事件、写空闲超时事件、读或者写超时事件,在接收到对应的空闲事件之后,灵活地进行定制。

2.2.2、内存保护机制

Netty提供多种机制对内存进行保护,包括以下几个方面。
◎通过对象引用计数器对Nety的 ByteBuf等内置对象进行细粒度的内存申请和释放,对非法的对象引用进行检测和保护。
◎通过内存池来重用 ByteBuf,节省内存。
◎可设置的内存容量上限,包括 ByteBuf、线程池线程数等。
AbstractReferenceCountedByteBuf的内存管理方法实现如图20-6、20-7所示。

ByteBuf的解码保护,防止非法码流导致内存溢出,代码如图20-8所示。

如果长度解码器没有单个消息最大报文长度限制,当解码错位或者读取到畸形码流时,长度值可能是个超大整数值,例如4294967296,这很容易导致内存溢出。如果有上限保护,例如单条消息最大不允许超过10MB,当读取到非法消息长度4294967296后,直接抛出解码异常,这样就避免了大内存的分配。

2.2.3、优雅停机

相比于Netty的早期版本,Nety5.0版本的优雅退出功能做得更加完善。优雅停机功能指的是当系统退出时,JVM通过注册的 Shutdown Hook拦截到退出信号量,然后执行退岀操作,释放相关模块的资源占用,将缓冲区的消息处理完成或者淸空,将待刷新的数据持久化到磁盘或者数据库中,等到资源回收和缓冲区消息处理完成之后,再退出。
优雅停机往往需要设置个最大超时时间T,如果达到T后系统仍然没有退出,则通过Kill -9 pid强杀当前的进程。
Netty所有涉及到资源回收和释放的地方都增加了优雅退出的方法,它们的相关接口如表20-1所示。

2.3、可定制性

Netty的可定制性主要体现在以下几点。
◎责任链模式: ChannelPipeline基于责任链模式开发,便于业务逻辑的拦截、定制和扩展
◎基于接口的开发:关键的类库都提供了接口或者抽象类,如果Nety自身的实现无法满足用户的需求,可以由用户自定义实现相关接口。
◎提供了大量工厂类,通过重载这些工厂类可以按需创建出用户实现的对象
◎提供了大量的系统参数供用户按需设置,增强系统的场景定制性。

2.4、可扩展性

基于Netty的基础NlO框架,可以方便地进行应用层协议定制,例如HTTP协议栈、Thrift协议栈、FTP协议栈等。这些扩展不需要修改 Netty的源码,直接基于Netty的二进制类库即可实现协议的扩展和定制。

目前,业界存在大量的基于Netty框架开发的协议,例如基于Netty的HTTP协议、Dubbo协议、 RocketMQ内部私有协议等。
 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty5.0 架构剖析和源码解读 作者:李林锋 版权所有 email neu_lilinfeng@ © Netty5.0 架构剖析和源码解读1 1. 概述2 1.1. JAVA 的IO演进2 1.1.1. 传统BIO通信的弊端2 1.1.2. Linux 的网络IO模型简介4 1.1.3. IO复用技术介绍7 1.1.4. JAVA的异步IO8 1.1.5. 业界主流的NIO框架介绍10 2.NIO入门10 2.1. NIO服务端10 2.2. NIO客户端13 3.Netty源码分析16 3.1. 服务端创建16 3.1.1. 服务端启动辅助类ServerBootstrap16 3.1.2. NioServerSocketChannel 的注册21 3.1.3. 新的客户端接入25 3.2. 客户端创建28 3.2.1. 客户端连接辅助类Bootstrap28 3.2.2. 服务端返回ACK应答,客户端连接成功32 3.3. 读操作33 3.3.1. 异步读取消息33 3.4. 写操作39 3.4.1. 异步消息发送39 3.4.2. Flush操作42 4.Netty架构50 4.1. 逻辑架构50 5. 附录51 5.1. 作者简介51 5.2. 使用声明51 1. 概述 1.1.JAVA 的IO演进 1.1.1. 传统BIO通信的弊端 在JDK 1.4推出JAVANIO1.0之前,基于JAVA 的所有Socket通信都采用 BIO 了同步阻塞模式( ),这种一请求一应答的通信模型简化了上层的应用开发, 但是在可靠性和性能方面存在巨大的弊端。所以,在很长一段时间,大型的应 C C++ 用服务器都采用 或者 开发。当并发访问量增大、响应时间延迟变大后, 采用JAVABIO作为服务端的软件只有通过硬件不断的扩容来满足访问量的激 增,它大大增加了企业的成本,随着集群的膨胀,系统的可维护性也面临巨大 的挑战,解决这个问题已经刻不容缓。 首先,我们通过下面这幅图来看下采用BIO 的服务端通信模型:采用BIO 通信模型的 1connect NewThread1 WebBrowse 2connect 2handle(Req) WebBrowse 3connect Acceptor NewThread2 WebBrowse WebBrowse 4connect NewThread3 3sendResponsetopeer NewThread4 图1.1.1-1 BIO通信模型图 服务端,通常由一个独立的Accepto 线程负责监听客户端的连接,接收到客户 端连接之后为客户端连接创建一个新的线程处理请求消息

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值