Netty原理分析
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。
Netty架构分析
Netty 采用了比较典型的三层网络架构进行设计,逻辑架构图如下所示:

第一层:Reactor 通信调度层,它由一系列辅助类完成,包括 Reactor 线程 NioEventLoop 以及其父类、NioSocketChannel/NioServerSocketChannel 以及其父类、ByteBuffer 以及由其衍生出来的各种 Buffer、Unsafe 以及其衍生出的各种内部类等。该层的主要职责就是监听网络的读写和连接操作,负责将网络层的数据读取到内存缓冲区中,然后触发各种网络事件,例如连接创建、连接激活、读事件、写事件等等,将这些事件触发到 PipeLine 中,由 PipeLine 充当的职责链来进行后续的处理。
第二层:职责链 PipeLine,它负责事件在职责链中的有序传播,同时负责动态的编排职责链,职责链可以选择监听和处理自己关心的事件,它可以拦截处理和向后/向前传播事件,不同的应用的 Handler 节点的功能也不同,通常情况下,往往会开发编解码 Hanlder 用于消息的编解码,它可以将外部的协议消息转换成内部的 POJO 对象,这样上层业务侧只需要关心处理业务逻辑即可,不需要感知底层的协议差异和线程模型差异,实现了架构层面的分层隔离。
第三层:业务逻辑处理层,可以分为两类:
1.纯粹的业务逻辑处理,例如订单处理。
2.应用层协议管理,例如HTTP协议、FTP协议等。
接下来,我从影响通信性能的三个方面(I/O模型、线程调度模型、序列化方式)来谈谈Netty的架构。
IO模型
Netty的I/O模型基于非阻塞I/O实现,底层依赖的是JDK NIO框架的Selector。
Selector提供选择已经就绪的任务的能力。简单来讲,Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。
线程调度模型
常用的Reactor线程模型有三种,分别如下:
1.Reactor单线程模型:Reactor单线程模型,指的是所有的I/O操作都在同一个NIO线程上面完成。对于一些小容量应用场景,可以使用单线程模型。

2.Reactor多线程模型:Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理I/O操作。主要用于高并发、大业务量场景。

3.主从Reactor多线程模型:主从Reactor线程模型的特点是服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。

序列化方式
影响序列化性能的关键因素总结如下:
1.序列化后的码流大小(网络带宽占用)
2.序列化&反序列化的性能(CPU资源占用)
3.并发调用的性能表现:稳定性、线性增长、偶现的时延毛刺等
链路有效性检测
心跳检测机制分为三个层面:
1.TCP层面的心跳检测,即TCP的Keep-Alive机制,它的作用域是整个TCP协议栈;
2.协议层的心跳检测,主要存在于长连接协议中。例如SMPP协议;
3.应用层的心跳检测,它主要由各业务产品通过约定方式定时给对方发送心跳消息实现。
心跳检测的目的就是确认当前链路可用,对方活着并且能够正常接收和发送消息。作为高可靠的NIO框架,Netty也提供了基于链路空闲的心跳检测机制:
1.读空闲,链路持续时间t没有读取到任何消息;
2.写空闲,链路持续时间t没有发送任何消息;
3.读写空闲,链路持续时间t没有接收或者发送任何消息。
零拷贝
“零拷贝”是指计算机操作的过程中, CPU不需要为数据在内存之间的拷贝消耗资源 。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而 直接在内核空间(Kernel Space)中传输到网络的方式
Netty的“零拷贝”主要体现在三个方面
Netty的 接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝 。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝
读取直接从“堆外直接内存”,不像传统的堆内存和直接内存拷贝
ByteBufAllocator 通过ioBuffer分配堆外内存
Netty提供了 组合Buffer对象 ,可以聚合多个ByteBuffer对象,用户可以 像操作一个Buffer那样方便的对组合Buffer进行操作 ,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer
Netty允许我们将多段数据合并为一整段虚拟数据供用户使用,而过程中不需要对数据进行拷贝操作
组合Buffer对象,避免了内存拷贝
ChannelBuffer接口:Netty为需要传输的数据制定了统一的ChannelBuffer接口
· 使用getByte(int index)方法来实现随机访问
· 使用双指针的方式实现顺序访问
· Netty主要实现了HeapChannelBuffer,ByteBufferBackedChannelBuffer,与Zero Copy直接相关的CompositeChannelBuffer类
CompositeChannelBuffer类
CompositeChannelBuffer类的作用是将多个ChannelBuffer组成一个虚拟的ChannelBuffer来进行操作
为什么说是虚拟的呢,因为CompositeChannelBuffer并没有将多个ChannelBuffer真正的组合起来,而只是保存了他们的引用,这样就避免了数据的拷贝,实现了Zero Copy,内部实现
其中readerIndex既读指针和writerIndex既写指针是从AbstractChannelBuffer继承而来的
components是一个ChannelBuffer的数组,他保存了组成这个虚拟Buffer的所有子Buffer
indices是一个int类型的数组,它保存的是各个Buffer的索引值
lastAccessedComponentId是一个int值,它记录了最后一次访问时的子Buffer ID
CompositeChannelBuffer实际上就是将一系列的Buffer通过数组保存起来,然后实现了ChannelBuffer 的接口,使得在上层看来,操作这些Buffer就像是操作一个单独的Buffer一样
Netty的文件传输采用了 transferTo方法 ,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题
Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都实现了零拷贝的功能,而在Netty中也通过在FileRegion中包装了NIO的FileChannel.transferTo()方法实现了零拷贝
Netty 的 Zero-copy 体现在如下几个个方面:
l Netty 提供了 CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。
l 通过 wrap 操作, 我们可以将byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作。
l ByteBuf 支持 slice 操作,因此可以将 ByteBuf 分解为多个共享同一个存储区域的ByteBuf, 避免了内存的拷贝。
l 通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题。
Netty原理分析的更多相关文章
- 《深入探索Netty原理及源码分析》文集小结
<深入探索Netty原理及源码分析>文集小结 https://www.jianshu.com/p/239a196152de
- 原理剖析-Netty之服务端启动工作原理分析(下)
一.大致介绍 1.由于篇幅过长难以发布,所以本章节接着上一节来的,上一章节为[原理剖析(第 010 篇)Netty之服务端启动工作原理分析(上)]: 2.那么本章节就继续分析Netty的服务端启动,分 ...
- jemalloc原理分析
netty4引入了内存池的概念,它的主要思想源自于jemalloc,由于难以理解netty中这一块的代码,我决定先看一看网上的相关文章 官方git jemalloc原理分析 jemalloc和内存管理 ...
- Netty原理架构解析
Netty原理架构解析 转载自:http://www.sohu.com/a/272879207_463994本文转载关于Netty的原理架构解析,方便之后巩固复习 Netty是一个异步事件驱动的网络应 ...
- Redisson 实现分布式锁的原理分析
写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题. 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有一个进程在持有锁的期间崩溃而未能主动释放锁, ...
- RocketMQ原理分析&场景问题
硬核干货分享,欢迎关注[Java补习课]成长的路上,我们一起前行 ! <高可用系列文章> 已收录在专栏,欢迎关注! 一.RocketMQ的基本原理 RocketMQ基本架构图如下 从这个架 ...
- Redisson 实现分布式锁原理分析
Redisson 实现分布式锁原理分析 写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题. 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有 ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- Java NIO使用及原理分析(1-4)(转)
转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
随机推荐
- SpringBoot------整合MyBatis
1.添加pom.xml需要的依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="htt ...
- 《转载》JVM垃圾回收机制
本文转载自ImportNew - 郑雯 每个Java程序员迟早都会碰到下面这个错误: java.lang.OutOfMemoryError 这个时候一般会建议采用如下方式解决这个错误: 增加MaxPe ...
- 【HTML5】HTML5中video元素事件详解(实时监测当前播放时间)
html 代码..video后边几个元素,可处理ios 系统的兼容性 <video id="myVideo" controls="controls" po ...
- 解决JS(Vue)input[type='file'] change事件无法上传相同文件的问题
Html <input id="file" type="file" accept=".map" onchange="uplo ...
- 初识springcloud
springcloud的基础是springboot,简单地说,就是通过写的springboot应用,使用springcloud集成. 在学习springcloud的过程中,自己的开发环境不能保证和博客 ...
- ubuntu下apache新建虚拟主机
最近发现在一个服务器上面布了一些项目,如果不用虚拟主机,用链接跳进去的话,有时候路径会出错,而自己在配置虚拟主机的时候又出现了一些问题,看似简单的东西,却花费了一上午,所以决定把简单的方法记下来,供和 ...
- java 三大框架 hibernate部分知识实现增删该查操作
1.三层架构 表现层 web层(MVC是一个表现层的设计模型) 业务层 service层 持久层 dao层2.三大框架和三层架构的关系(建议学习三大框架的顺序:先学习hibernat ...
- Oracle出现高占内存的解决办法:
因为安装Oracle时,为了均衡电脑性能和数据库性能,默认内存大小为物理内存的1/8,自身内存比较大时,oracle所占的内存也会变大.而通常,我们自己的环境并不需要分配那么大的内存来支持Oracle ...
- juqery 点击分页显示,指定一页显示多少个,首次加载显示多少个
源代码html: //源代码:html <div class="jq22"> <div class="hidden"> <li&g ...
- IO流实例
//字节流: import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;impor ...