Netty是由JBOSS提供的一个Java开源框架,Netty是一个高性能、异步事件驱动的NIO框架,提供了对TCP、UDP和文件传输的支持,可用于快速开发可维护的高性能协议服务器和客户端。
Netty是基于Java NIO的一个C/S网络应用框架,所有IO操作都是异步非阻塞的,通过Future-Lisener机制,用户可以很方便的主动获取或者通过通知机制获得IO操作结果。
API简单,开发门槛低。
功能强大,预置了多种编解码功能,支持多种主流协议。
定制能力强,可以通过ChannelHandler对通信框架进行灵活的扩展。
性能高,通过与其他业界主流的NIO框架对比Netty的综合性能是最优之一。
传输快,正是利用了零拷贝的特点。
成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要在为NIO的BUG而烦恼。
互联网行业:在分布式系统中,用于实现各个进程节点之间的内部通信。
游戏行业:高性能的基础通信组件,非常方便的定制和开发私有协议栈。
大数据:经典Hadoop高性能通信和序列化组件Avro的RPC框架,默认就是采用Netty进行跨节点通信。
备注:Hadoop、Spark、Scala通信模型、RocketMQ、Dubbox等框架的底层通信都是用Netty。
volatile的大量和正确的使用。
CAS和原子类的使用。
线程安全容器的使用。
通过读写锁提升并发性能。
零拷贝、缓冲区、内存池。
高性能的序列。
备注:参考系列文章详解。
BIO(阻塞、流):一个链接一个线程,可以用伪异步的方式去优化。
NIO(非阻塞、缓冲区):一个请求一个线程,但客户端发送的链接请求都会注册到多路复用器,多路复用器轮询到链接IO请求时启动一个线程去处理。
AIO:一个有效请求一个线程,客户端的IO请求都是由IO完成了再通知服务器应用去启动线程处理。
备注:参考系列文章详解。
Buffer、Channel、Selector。
备注:参考系列文章详解。
Reactor是反应堆的意思,这个模型是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。即服务端程序处理传入的多路请求,并将它们同步分派给请求对应的处理线程。
Reactor模式也叫作Dispatcher模式,即IO多了复用统一监听事件,收到事件后分发(Dispatch给某进程),是编写高性能网络服务器的技术之一。
即Reactor模式是事件驱动的,有一个或多个并发输入源,有一个ServiceHandler,有多个RequestHandlers。
Netty是基于主从Reactors多线程模型做了一些修改,其中的Reactor多线程模型有多个Reactor:MainReactor和SubReactor。
MainReactor:负责客户端的链接请求,并将请求转发给SubReactor。
SubReactor:负责相应通道的IO读写请求。
非IO请求(具体业务逻辑处理):这种任务则会直接写入队列,等待Worker Threads进行处理。
备注:参考系列文章详解。
常见产生原因:
1、应用程序Write写入的字节大小大于套接字发送的缓冲区的大小。
2、进行MSS大小的TCP分段
3、以太网帧的payload大于MTU进行IP分片。
常见主流解决方案:
1、消息定长:例如每个包的大小固定为200个字节,不够用空格补。
2、特殊字符:在包尾部增加特殊字符进行分割,例如加回车等,在实际中经常使用的是这种。
3、自定义:将消息分为消息头和消息体,消息头中包含消息总长度的字段,然后进行业务逻辑的处理,但是要考虑安全等问题。
备注:参考系列文章详解。
就是在传输文件时,不需要将文件内容拷贝到用户空间,而是直接在内核空间中传输到网络的方式,避免了用户空间和内存空间之间的拷贝,从而提升了系统的整体性能。
Netty中的零拷贝技术:
1、Netty接收和发送ByteBuffer采用Direct Buffers(直接缓冲),使用对外直接内存进行Socket读写(JVM篇),不需要进行字节缓冲区的二次拷贝,如果使用传统的堆内存(Heap Buffers)进行Soket读写,JVM会将对内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
2、Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个BUffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大Buffer。
3、Netty的文件传输采用了transferTo方法,可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环wirte方式导致的内存拷贝问题。
备注:参考系列文章详解。
ServerBootstrap、Bootrap:服务端和客户端配置整个Netty程序,串联各个组件。
Channel:能够用于执行网络I/O操作,Channel为用户提供了,1、当前网络链接的通道的状态、2、网络链接的配置参数、3、提供网络IO操作(如建立端口、读写、绑定端口),以及IO操作处理程序。
NioEventLoopGroup:管理EventLoop的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)负责处理多个Channel上的事件,而一个Channel只对应一个线程。
ChannelHandler:处理IO事件或拦截IO操作,并将其准发到ChannelPipeline(业务处理链)的下一个处理程序。
ChannelPipline:保存了ChannleHandler的List,用于处理或拦截Channel的入栈事件和出站操作,使用户可以完全控制事件的处理方式,以及Channel中各个的ChannelHandler如何相互交互。
两种方式:
写入Channel,消息从ChannelPipeline当中尾部开始移动。
写入ChannelHandler绑定的ChannelHandlerContext中,消息从ChannelPipeline中的下一个ChannelHandler中移动。
Netty默认是CPU处理数的两倍,bind完之后启动。
序列化的目的就有两个,一是网络传输、而是对象持久化。
虽然可以使用Java进行对象序列化,Netty去传输,但是Java序列化的硬伤太多了,且明显,比如没法跨语音,序列化后码流太大,序列化性能低等等。
JBOSS的Marshalling:对JDK默认的序列化框架进行了优化,但又保持了跟java.io.Serializable接口的兼容,同时增加了一些可选的参数和附加特性。
Google的Protobuf:基于Protobuf协议,序列号后码流小,性能高,依赖于工具生成代码,支持语音也比较少,但是在性能要求高的RPC调用,而且具有很好的跨防火墙的访问属性,适合应用程对象持久化。
基于Protobuf的Kyro(Spark):只支持java语言。
MessagePack框架:高效的二进制序列化格式。
Avro:Hadoop的一个子项目,解决了JSON的冗长和没有IDL的问题,对于习惯于静态类型语音的用户不直观,适用于Hadoop中的Hive这些做持久化数据格式。
Thrift:是序列化协议也是框架,体积小,速度快,使用少,不安全,不具备可读性,不适合用于数据持久化的协议。
XML:可读性好,文件大。
JSON:轻量,可读性好,描述性比XML差,额外空间开销大,不适合ms级别的情况。
Fastjson:采用假定有序快速匹配算法,目前java最快json库之一,但是过度注重快,偏离了标准,代码质量不高。
Thrift:是序列化协议也是框架,体积小,速度快,使用少,不安全,不具备可读性,不适合用于数据持久化的协议。
内部系统,性能要求100ms以上的服务,基于XML的SOAP协议是值得考虑。
基于Web浏览器交互的Ajax、手机等跟服务端通讯的,或动态类型语言,JSON是首选
调试环境恶劣的,JSON或XML能够提高效率,降低成本。
性能和简洁性极高要求,Protobuf、Thrift、Avro可以考虑。
T级别的数据持久化场景,Protobuf和Avro是首选,其中Protobuf符合静态类型语言场景,Avro适合动态语言场景。
完整的RPC解决方案,可以考虑用Thrift。
readerldleTime:读超时时间,即一定时间内没有接收到消息。
writerldleTime:写超时时间,即一定时间内向测试端发送消息。
allldleTime:所有类型的超时时间。
Tomcat是Web容器;Netty是异步事件驱动的网络编程框架,例如TCP和UDP套接字服务器。
Tomcat是基于Http的Web服务器;Netty能通过编程自定义各种协议,因为Netty本身自己能编码/解码字节流,所有Netty都可以实现,如Http、FTP、UDP、RCP、WebSocket、Proxy服务器等。
首先会申请一大块内存Arena,Arena由许多的Chunk组成,而每个Chunk默认由2048个page组成。
Chunk通过AVL树(平衡二叉树)形式组成Page,每一个叶子节点表示一个Page,而中间节点表示内存区域,节点主机记录它在整个Arena中的偏移地址。
当区域给分配出去后,中间节点上的标记位会被标记,这样就标识这个中间节点以下的所有节点都已被分配了。
对于大于8K的内存分配在poolChunkList中,而PoolSubpage用于分配小于8k的内存,它会把一个page分割成多个段,进行内存分配。
支持自动化扩容(4M),通过内置的符合缓冲类型,实现零拷贝;不需要调用flip()来切换读写模式,读取和写入索引分开;引用计数基于AtomicIntegerFiledUpdateer用于内存回收;PooledByteBuf采用二叉树实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象。