Netty框架总体架构及源码知识点

Netty概述

Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持。做为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通讯行业等得到了普遍的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。html

Netty 利用 Java 高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 构建一个客户端/服务端,其具备高并发、传输快、封装好等特色。react

高并发面试

Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通讯框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能获得了很大提升 。数据库

传输快编程

Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。后端

封装好安全

Netty封装了NIO操做的不少细节,提供易于使用的API,还有心跳、重连机制、拆包粘包方案等特性,使开发者能可以快速高效的构建一个稳健的高并发应用。服务器

Netty框架总体架构及源码知识点

image网络

Netty框架

Netty项目致力于提供一个异步的、事件驱动的网络应用框架和工具,用于快速开发可维护的、高性能的、高扩展性的服务器和客户端之间的协议。换句话说,Netty式一个NIO客户端服务器框架,可以快速、轻松地开发网络应用例如服务器和客户端间的协议。它简化了网络编程如TCP/IP socket服务器。多线程

JBOSSes Netty的设计吸收了大量的协议如FTP、SMTP、HTTP和各类二进制、基于文本的继承协议等协议的设计经验,成功地找到了一种方法实现易于开发、性能、稳定、灵活的协议开发。

特征:

Netty为用户提供了不少创新和更好的网络开发体验。

1)设计Design

为各类传输类型(块和非块socket)提供了统一的API;

创建在灵活和可扩展的事件模型;

高度可定制的线程模式——单线程,一个或多个线程池(如SEDA);

可信的五链接数据报socket支持。

2)易于使用

良好文档化的Javadoc、用户向导和例子;

结构并不臃肿;

无其它的依赖,只需JDK1.5或以上。

3)性能

高吞吐量、低延迟时间;

很小的资源消耗;

最小化没必要要的内存复制。

4)健壮性

不会由于快速链接、慢速链接或超载链接引发OutOfMemoryError错误;

高速网络下不会引发NIO程序的读写异常。

5)安全

彻底支持SSL/TLS和StartTLS;

在Java Applet环境下运行正常。

6)社区

至少每两周一个版本发布。

项目主页: http://www.jboss.org/netty/

文档地址: http://www.jboss.org/netty/documentation.html

下载地址: http://www.jboss.org/netty/downloads.html

为何选择Netty

Socket通讯(IO/NIO/AIO)编程,对于通讯模型已经有了一个基本的认识。咱们学习的仅仅是一个模型,若是想把这些真正的用于实际工做中,那么还须要不断的完善、扩展和优化。好比经典的TCP读包写包问题,或者是数据接收的大小,实际的通讯处理与应答的处理逻辑等等一些细节问题须要认真的去思考,而这些都须要大量的时间和经历,以及丰富的经验。因此想学好Socket通讯不是件容易事,那么接下来就来学习一下新的技术Netty,为何会选择Netty?由于它简单!使用Netty没必要编写复杂的逻辑代码去实现通讯,不再须要去考虑性能问题,不须要考虑编码问题,半包读写等问题。强大的Netty已经帮咱们实现好了,咱们只须要使用便可。

Netty是最流行的NIO框架,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是数一数二的。它已经获得成百上千的商业/商用项目验证,如Hadoop的RPC框架Avro、RocketMQ以及主流的分布式通讯框架Dubbox等等。

Netty框架总体架构及源码知识点

image

Netty的线程模型

并发系统能够采用多种并发编程模型来实现。并发模型指定了系统中的线程如何经过协做来完成分配给它们的做业。不一样的并发模型采用不一样的方式拆分做业,同时线程间的协做和交互方式也不相同。

对于网络请求通常能够分为两个处理阶段,一是接收请求任务,二是处理网络请求。根据不一样阶段处理方式分为如下几种线程模型:

串行化处理模型

这个模型中用一个线程来处理网络请求链接和任务处理,当worker接受到一个任务以后,就马上进行处理,也就是说任务接受和任务处理是在同一个worker线程中进行的,没有进行区分。这样作存在一个很大的问题是,必需要等待某个task处理完成以后,才能接受处理下一个task。

而一般状况下,任务的处理过程会比任务的接受流程慢得多。例如在处理任务的时候,咱们可能会须要访问远程数据库,这属于一种网络IO。一般状况下IO操做是比较耗时的,这直接影响了下一个任务的接受,并且一般在IO操做的时候,CPU是比较空闲的,白白浪费了资源。

所以能够把接收任务和处理任务两个阶段分开处理,一个线程接收任务,放入任务队列,另外的线程异步处理任务队列中的任务。

并行化处理模型

因为任务处理通常比较缓慢,会致使任务队列中任务积压长时间得不处处理,这时可使用多线程来处理。这里使用的是一个公共的任务队列,多线程环境中难免要经过加锁来保证线程安全,咱们经常使用的线程池就是这种模式。能够经过为每一个线程维护一个任务队列来改进这种模型。

Reactor线程模型

reactor线程模型关注的是:任务接受以后,对处理过程继续进行切分,划分为多个不一样的步骤,每一个步骤用不一样的线程来处理,也就是本来由一个线程处理的任务如今由多个线程来处理,每一个线程在处理完本身的步骤以后,还须要将任务转发到下阶段线程继续进行处理。

Netty的Reactor线程模型

其中mainReacotor,subReactor,Thread Pool是三个线程池。mainReactor负责处理客户端的链接请求,并将accept的链接注册到subReactor的其中一个线程上;subReactor负责处理客户端通道上的数据读写;Thread Pool是具体的业务逻辑线程池,处理具体业务。

Netty具体线程模型

如何理解NioEventLoop和NioEventLoopGroup

1)NioEventLoop实际上就是工做线程,能够直接理解为一个线程。NioEventLoopGroup是一个线程池,线程池中的线程就是NioEventLoop。

2)实际上bossGroup中有多个NioEventLoop线程,每一个NioEventLoop绑定一个端口,也就是说,若是程序只须要监听1个端口的话,bossGroup里面只须要有一个NioEventLoop线程就好了。

每一个NioEventLoop都绑定了一个Selector,因此在Netty的线程模型中,是由多个Selecotr在监听IO就绪事件。而Channel注册到Selector。

一个Channel绑定一个NioEventLoop,至关于一个链接绑定一个线程,这个链接全部的ChannelHandler都是在一个线程中执行的,避免了多线程干扰。更重要的是ChannelPipline链表必须严格按照顺序执行的。单线程的设计可以保证ChannelHandler的顺序执行。

一个NioEventLoop的selector能够被多个Channel注册,也就是说多个Channel共享一个EventLoop。EventLoop的Selecctor对这些Channel进行检查。

在监听一个端口的状况下,一个NioEventLoop经过一个NioServerSocketChannel监听端口,处理TCP链接。后端多个工做线程NioEventLoop处理IO事件。每一个Channel绑定一个NioEventLoop线程,1个NioEventLoop线程关联一个selector来为多个注册到它的Channel监听IO就绪事件。NioEventLoop是单线程执行,保证Channel的pipline在单线程中执行,保证了ChannelHandler的执行顺序。

小编准备了关于netty的面试题分享给你们,因为文章篇幅缘由如下只分享10道netty的面试题。后五道题未设置答案,须要获取答案和更多Java架构资料、面试题(含答案)和面试心得以及视频资料的能够加入Java猫的架构学习基地:810589193获取!

netty面试题

1.BIO、NIO和AIO的区别?

  • BIO:一个链接一个线程,客户端有链接请求时服务器端就须要启动一个线程进行处理。线程开销大。
  • 伪异步IO:将请求链接放入线程池,一对多,但线程仍是很宝贵的资源。
  • NIO:一个请求一个线程,但客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有I/O请求时才启动一个线程进行处理。
  • AIO:一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
  • BIO是面向流的,NIO是面向缓冲区的;BIO的各类流是阻塞的。而NIO是非阻塞的;BIO的Stream是单向的,而NIO的channel是双向的。
  • NIO的特色:事件驱动模型、单线程处理多任务、非阻塞I/O,I/O读写再也不阻塞,而是返回0、基于block的传输比基于流的传输更高效、更高级的IO函数zero-copy、IO多路复用大大提升了Java网络应用的可伸缩性和实用性。基于Reactor线程模型。
  • 在Reactor模式中,事件分发器等待某个事件或者可应用或个操做的状态发生,事件分发器就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来作实际的读写操做。如在Reactor中实现读:注册读就绪事件和相应的事件处理器、事件分发器等待事件、事件到来,激活分发器,分发器调用事件对应的处理器、事件处理器完成实际的读操做,处理读到的数据,注册新的事件,而后返还控制权。

2.NIO的组成?

  • Buffer:与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的
  • flip方法 : 反转此缓冲区,将position给limit,而后将position置为0,其实就是切换读写模式
  • clear方法 :清除此缓冲区,将position置为0,把capacity的值给limit。
  • rewind方法 : 重绕此缓冲区,将position置为0
  • DirectByteBuffer可减小一次系统空间到用户空间的拷贝。但Buffer建立和销毁的成本更高,不可控,一般会用内存池来提升性能。直接缓冲区主要分配给那些易受基础系统的本机I/O 操做影响的大型、持久的缓冲区。若是数据量比较小的中小应用状况下,能够考虑使用heapBuffer,由JVM进行管理。
  • Channel:表示 IO 源与目标打开的链接,是双向的,但不能直接访问数据,只能与Buffer 进行交互。经过源码可知,FileChannel的read方法和write方法都致使数据复制了两次!
  • Selector可以使一个单独的线程管理多个Channel,open方法可建立Selector,register方法向多路复用器器注册通道,能够监听的事件类型:读、写、链接、accept。注册事件后会产生一个SelectionKey:它表示SelectableChannel 和Selector 之间的注册关系,wakeup方法:使还没有返回的第一个选择操做当即返回,唤醒的缘由是:注册了新的channel或者事件;channel关闭,取消注册;优先级更高的事件触发(如定时器事件),但愿及时处理。
  • Selector在Linux的实现类是EPollSelectorImpl,委托给EPollArrayWrapper实现,其中三个native方法是对epoll的封装,而EPollSelectorImpl. implRegister方法,经过调用epoll_ctl向epoll实例中注册事件,还将注册的文件描述符(fd)与SelectionKey的对应关系添加到fdToKey中,这个map维护了文件描述符与SelectionKey的映射。
  • fdToKey有时会变得很是大,由于注册到Selector上的Channel很是多(百万链接);过时或失效的Channel没有及时关闭。fdToKey老是串行读取的,而读取是在select方法中进行的,该方法是非线程安全的。
  • Pipe:两个线程之间的单向数据链接,数据会被写到sink通道,从source通道读取
  • NIO的服务端创建过程:Selector.open():打开一个Selector;ServerSocketChannel.open():建立服务端的Channel;bind():绑定到某个端口上。并配置非阻塞模式;register():注册Channel和关注的事件到Selector上;select()轮询拿到已经就绪的事件

3.Netty的特色?

  • 一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持
  • 使用更高效的socket底层,对epoll空轮询引发的cpu占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。
  • 采用多种decoder/encoder 支持,对TCP粘包/分包进行自动化处理
  • 可以使用接受/处理线程池,提升链接效率,对重连、心跳检测的简单支持
  • 可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,经过内存池的方式循环利用ByteBuf
  • 经过引用计数器及时申请释放再也不引用的对象,下降了GC频率
  • 使用单线程串行化的方式,高效的Reactor线程模型
  • 大量使用了volitale、使用了CAS和原子类、线程安全类的使用、读写锁的使用

4.Netty的线程模型?

  • Netty经过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。
  • 单线程模型:全部I/O操做都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。既要接收客户端的链接请求,向服务端发起链接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上没法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。
  • 多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 链接请求;NIO 线程池负责网络IO 的操做,即消息的读取、解码、编码和发送;1 个NIO 线程能够同时处理N 条链路,可是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操做问题。但在并发百万客户端链接或须要安全认证时,一个Acceptor 线程可能会存在性能不足问题。
  • 主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端链接,将SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,从新注册到Sub 线程池的线程上,用于处理I/O 的读写等操做,从而保证mainReactor只负责接入认证、握手等操做;

5.TCP 粘包/拆包的缘由及解决方法?

  • TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分红多个包进行发送,也可能把小的封装成一个大的数据包发送。
  • TCP粘包/分包的缘由:
  • 应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用屡次写入的数据发送到网络上,这将会发生粘包现象;
  • 进行MSS大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包
  • 以太网帧的payload(净荷)大于MTU(1500字节)进行ip分片。
  • 解决方法
  • 消息定长:FixedLengthFrameDecoder类
  • 包尾增长特殊字符分割:行分隔符类:LineBasedFrameDecoder或自定义分隔符类 :DelimiterBasedFrameDecoder
  • 将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。

6.了解哪几种序列化协议?

7.如何选择序列化协议?

8.Netty的零拷贝实现?

9.Netty的高性能表如今哪些方面?

10.NIOEventLoopGroup源码?

若是你想突破本身的天花板,那必定要比别人付出更多,这个过程是很辛苦的。若是你认准了一条路,坚持走下去,你必定会得到不少收获和你满意的答案。

相关文章
相关标签/搜索