【NIO系列】——之Netty

这是NIO系列的第四篇,欢迎继续关注:java

  1. 【NIO系列】——之TCP探秘
  2. 【NIO系列】——之IO模型
  3. 【NIO系列】——之Reactor模型

若是你看过前面三篇文章,咱们从最低层来分解NIO底层原理和使用方式,帮忙咱们理解了NIO是什么,解决了什么问题,以及又有那些不足。web

原则上NIO的出现,已经提高和加快了网络IO的处理方式,但它只能帮忙咱们解决了IO层次的读写问题,在软件层次上咱们须要更好的编程架构模型,来解决扩展性以及高并发的问题。Netty正是用来解决这些问题的,这一篇咱们将详细介绍Netty的知识点。编程

 

1、netty是什么

一、是什么

咱们采用官方的话来讲:tomcat

Netty是一个高性能、异步事件驱动的网络应用框架。 基于Netty,能够快速的开发和部署高性能、 高可用的网络服务端和客户端应用。安全

简单来讲,它是一个网络应用框架,帮你解决面向网络开发中的三个问题:服务器

  • 面向网络IO的读写,如TCP的socket链接网络

  • 应用层协议编解码,如HTTP协议多线程

  • 高并发架构架构

那么他有哪些优势?并发

  • Fast —性能强悍,高并发,低延迟

  • Easy —高扩展,API使用简单,开发门槛低

  • Non-blocking — 无阻塞,支持NIO

 

二、有何不一样

对于咱们用惯了web容器的人来讲,第一个疑惑就是netty究竟能干什么,为什么要用它。既然Netty是一个网络应用框架,这些明明tomcat也能帮咱们解决了,为什么还要用Netty?

netty和tomcat的不一样,主要在如下几点:

  • tomcat是一个「HTTP Server」,更准确的说是一个「Servlet/JSP」应用的容器,它主要解决的是 HTTP 协议层面的传输和访问。

  • HTTP是一种应用层协议,应用层的协议除了HTTP之外,还有面向邮局协议的POP和IMAP,以及FTP、LDAP、SSH、TLS/SSL等其余协议。

  • Netty 不只能够支持HTTP,还能够支持FTP、LDAP、SSH、TLS/SSL等应用层多数的协议,另外还支持自定义的应用层协议,只要你有这方面的需求,它足够灵活。

  • Netty 虽然按归类上算属于OSI的第七层【应用层】,但它的存在是帮你支持第三层【传输层】的问题,好比面向TCP,面向UDP,以及SCTP网络协议的开发,它都能很好的支持。因此他能够称为一个通讯组件。

  • 原则上,Tomcat的网络通讯组件也能够采用Netty,但servlet3.0以前彻底是同步阻塞模型,tomcat要遵循servlet规范因此不能最大发挥NIO的特性,而Netty不用遵循servlet规范,能够最大化发挥NIO的特性,性能更高一些。

 

2、为什么要用Netty

Netty的三大特性(fast,easy,no-blocking)能够详细分解为如下特性:

  • 支持NIO,异步编程

  • 性能强悍,高并发,低延迟,更少的资源占用和内存使用率带来更快的性能支撑,NIO领域最强

  • 成熟、稳定,你想到的一切tcp 问题,都已经解决,特别是NIO bug,以及完美解决了。

  • 不只仅支持http,支持多种应用协议和网络协议,如TCP/UDP/UDT/SCTP/FTP/SMTP等。

  • 功能强大,预制了多种编解码处理器,支持多种主流应用协议

  • API使用简单,开发门槛低

 

1.Fast

为什么快,能够参考上篇文章(【NIO系列】——之Reactor模型)介绍的Reactor IO模型。

 

Netty正是基于NIO实现了这种Reactor模型,Boss线程用来专门处理链接的创建,SubReactor专门用来处理IO的读写以及任务的处理。这种线程模型在充分利用CPU性能的状况下支撑大量的并发链接。

 

具体效果如何,咱们看下测试数据:

以上是在techempower获取的关于webFrame的纯文本响应测试,能够看到netty在响应速度方面,排名第二,虽然近几年高性能的web框架不断的挑战,netty排名有所降低,但依然还能保持前列位置。

 

以上是在dubbo的官方测试案例中,给定协议序列化TPS对比,能够看到采用netty的dubbo,在tps方面名列前茅。

 

2.更少的内存使用

Netty由于要面对大量的网络数据包处理,因此会有大量的网络对象建立和销毁,对JVM来讲有很大的压力。

Netty主要采用两种方案来缓解JVM的压力:

  • ByteBufAllocator 对象池。池化ByteBuf实例以提升性能并最小化内存碎片,后者每次调用时都返回一个新的实例。

  • 零拷贝。支持DirectBuffer的使用,经过JVM的本地调用分配内存,这可避免每次调用本地I / O操做以前(或以后)将缓冲区的内容复制到(或从)中间缓冲区。

如下是twitter对应Netty4的使用对象池应用压测:

 

3.Easy,快速开发

咱们知道牵扯到网络上的编程会比较复杂,除了有一堆须要设置的TPC参数之外,还有一堆IO读写的问题要处理,同时为了提升并发能力,还采用多线程,这就又带来了线程安全的问题,每每给开发者带来了很大的挑战和复杂度。

而Netty 正是针对这些难点统一作了简易化的封装,除了让API更加简单易用之外,好比:TCP服务器的配置启动,数据包buffByte的读写等。另外基于事件模式,对网络事件进行串行处理,在保证了高效的同时,又下降了编程的复杂度。

为什么串行化执行会提高netty的性能:

  • 串行无锁化设计,在IO线程内部进行串行操做,避免多线程竞争致使的性能降低。

  • 经过调整NIO线程池的线程参数,能够同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工做线程模型性能更优

  • 减小上下文切换,以及状态数据的同步

 

如下 是ChannelPipeline用来处理网络事件的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转。支持插拔模式,扩展性也很强。

 

4.可靠稳定

在网络方面,应用经常要面对复杂的网络环境,好比网络环境差,常会出现一些网络闪断,单通以及网络阻塞等等一系列问题。另外做为基础的通讯组件,还须要考虑健壮性的问题,由于一旦出现bug,则会致使依赖的大量业务中断。

Netty 在版本迭代中不断的加入一些可靠性特性来知足用户日益高涨的可靠性与健壮性需求。好比NIO的select空转bug,好比TCP的断线重连,keep-alive检测等问题,Netty已经帮你解决了。

特别是Netty在推出4.0以后,已经被各家大厂采纳为通讯组件,这是对其可靠性验证,也是社区对其稳定性的承认。如下是在使用netty的一些公司,更多参考netty.Adopters

 

3、如何用

上面介绍了这么多Netty如何厉害,到编码阶段不会出现一堆代码来要处理吧。

咱们来一个简单的TCPServer来进行示例.

 

需求:

Client:接收用户输入的内容,并发送给Server,同时收并显示Server返回的内容

Server:监听8088端口,接收并显示Client发送的内容,并给Client相应的回复

 

代码:

// step 1 设置boss 和 work
      EventLoopGroup bossGroup = new NioEventLoopGroup();
      EventLoopGroup workerGroup = new NioEventLoopGroup();
      try {
          // step 2 服务启动辅助类
          ServerBootstrap b = new ServerBootstrap(); 
          // 设置线程池
          b.group(bossGroup, workerGroup)
              // step3 设置channel
              .channel(NioServerSocketChannel.class) 
              // setp4 设置channel hanlder
              .childHandler(new ChildChannelHandler())
              //设置发送和接受缓冲区大小
              .option(ChannelOption.SO_SNDBUF,256)
              .option(ChannelOption.SO_RCVBUF,256)
              // 小封包自动链接
              .option(ChannelOption.TCP_NODELAY,true)
              .childOption(ChannelOption.SO_KEEPALIVE, true)
              .handler(new LoggingHandler(LogLevel.INFO));
          // step 7 Bind 监听端口.
          ChannelFuture f = b.bind(PORT).sync(); 
          logger.info("tcp server start success... ");
          f.channel().closeFuture().sync();
      } finally {
      }

ChildChannelHander 扩展Pipeline的执行策略:

protected void initChannel(SocketChannel channel) throws Exception {
      ChannelPipeline pipeline = channel.pipeline();
      pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
      pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
      pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
      pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
      pipeline.addLast(new TcpHelloServerHandler());
​
  }

 

经过以上代码,咱们大概须要经过7个步骤,就能建立一个TCP的Server服务器了,看似7步较多,其实看代码量来讲,已经不多了。

经过以上的执行流程图,咱们能够看到:

  • ServerBootstrap是服务启动(引导)辅助类,采用无参构造器(Builder模式)提供一系统方法用于设置启动相关的参数

  • EventLoopGroup:Netty的Reactor线程池,负责维护一组EventLoop的调度工做,一般是bossEventLoop用来维护链接,subEventLoop来维护全部已注册的Channel的I/O操做处理用户自定义的Task和定时任务的Task

  • Channel:对Java原生的NIO类库进行了封装对通常用户而言,不须要关心底层实现细节和工做原理,只须要指定具体使用哪一种Channel,用以链接IO设备(socket)的纽带,提供与IO设备异步I/O操做的支持(如读、写、链接和绑定)

  • ChildChannelHandler,用来配置ChannelPipeline的执行策略。用来扩展的关键接口,用户能够完成大多数的功能定制,如消息编解码、心跳、安全认证、TSL/SSL认证、流量控制等。

 

4、最后

以上咱们经过Netty 是什么、怎么用、为什么用三个方面阐述了Netty的优点以及基本的介绍,想必可以对Netty作一个基本的了解。后续咱们将对Netty高性能架构,以及多线程高效编程方面,结合源码,会有更多的剖析。咱们不只仅关注源码是如何实现了,还会结合整个架构,来分析设计理念,以及探讨鲁棒性很强的代码的编写方式。如有兴趣,欢迎持续关注。

 

更多架构知识,欢迎关注个人公众号,大码候(cool_wier)