Netty指南(3)--- Netty入门

前言

本文我们将会介绍什么是Netty,为什么要选择Netty,以及Netty的特性。

什么是Netty

Netty是一个Java语言开发的高性能非阻塞I/O client/server框架,它支持快速、简单的开发client/server网络应用程序,大大简化了网络编程,如:TCP、UDP套接字服务器等。
Netty的”快速而简单”并不意味着使用它开发出的应用程序会遭遇可维护性或性能问题,Netty经过非常精心的设计(也许这就是艺术),积累了多种协议(FTP、SMTP、HTTP、各种二进制协议和基于文本的遗留协议)实现的经验。

选择Netyy的理由

为什么不使用原生NIO类库进行开发

  • NIO类库和API繁杂且使用麻烦,需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  • 需要具备其他的额外技能做铺垫,例如熟悉Java多线程编程,这是因为NIO编程设计Reactor模式,必须对多线程和网络编程非常熟悉,才能编写出高质量的NIO程序。
  • 可靠性能力补齐,工作量和难度都非常大,例如客户端重连、半包读写、失败缓存、异常码流的处理等问题,且还需要很好的模块设计才能保证代码更改起来不会牵一发动全身。
  • JDK NIO的BUG,例如epoll bug,它会导致Selector空轮询,最终导致CPU 100%,官方称JDK 1.6(6u4)版本修复了该问题,但直到JDK 1.7版本该问题依旧存在,只是发生概率降低了而已,如果你想了解该bug的细节,可以点击下面两个链接查看。
    点击查看JDK NIO epoll bug详情
    点击查看JDK NIO epoll bug详情

    为什么选择Netty

  • Netty是业界流行的NIO框架之一,它的健壮性、功能、性能、可定制和可扩展性是目前NIO框架中最好的,它已经得到成千上万的商用项目验证。
  • 统一的API,使用简单,支持多种传输类型,阻塞或非阻塞的,内置了多种编解码功能,支持多种主流协议。
  • 易于使用,完备的Javadoc和大量的示例集。
  • 高性能,拥有比Java的核心API更高的吞吐量以及更低的延迟,得益于池话和服用,拥有更低的资源消耗,最少的内存复制。
  • 健壮性强,不会因为慢速、快速或超载的连接而导致OutOfMemoryError,消除在高速网络中NIO应用程序常见的不公平读/写问题。
  • 安全性,完整的SSL/TSL/StartTLS支持
  • 定制能力强,可以通过ChannelHandler对通信框架进行灵活的扩展。
  • 成熟、稳定,Netty修复了已经被发现的所有JDK NIO Bug,业务开发人员不需要为JDK NIO本身存在的问题而苦恼。
  • 社区活跃,版本迭代周期短,发现Bug能被及时修复。

使用JDK NIO开发与使用Netty的对比

下面是两种方式编写一个非常简单的NIO程序的流程,流程中不涉及TCP粘包拆包和编解码细节。

使用JDK NIO的开发流程

  • 创建ServerSocketChannel,并配置它为非阻塞模式;
  • 绑定监听,配置TCP参数,例如backlog、sndbuf等;
  • 创建一个独立的I/O线程,用于轮询Selector;
  • 创建Selector,将之间创建的ServerSocketChannel注册到Selector上,监听SelectionKey.ACCEPT;
  • 启动I/O线程,再循环体重执行Selector.select()方法,轮询就绪的Channel;
  • 当轮询到了就绪状态的Channel是,需要判断其状态,如果是ACCEPT状态,说明是新接入的客户端,则调用ServerSocketChannel.accept()接受客户端连接;
  • 将SocketChannel注册到Selector上,监听OP_READ操作位;
  • 如果Channel状态为OP_READ,则说明SocketChannel有新的就绪的数据包需要读取,则构造ByteBuffer读取数据包;
  • 如果Channel状态为OP_WRITE,说明还有数据没有发送完成,需要继续发送。

    使用Netty NIO的开发流程

  • 创建 NIO 线程组 EventLoopGroup 和 ServerBootstrap。
  • 设置 ServerBootstrap 的属性:线程组、SO_BACKLOG 选项,设置 NioServerSocketChannel 为 Channel,设置业务处理 Handler。
  • 绑定端口,启动服务器程序。
  • 在业务处理 TimeServerHandler 中,读取客户端发送的数据,并给出响应。
    可以看到,Netty把NIO开发中的很多细节帮我们做了,比如:不需要操作Selector,不需要手动从SocketChannel中读取数据并转换完ByteBuffer等问题,我们只需要考虑业务处理就可以了。
    从语言描述上体会可能不是很深,你可以点击下方连接查看代码,与之前的JDK NIO代码进行对比,你会更直观的体会出Netty的”简单与快速”。
    点击查看Netty NIO完整示例代码

Netty核心组件

以下组件目前有个概念即可,后续会有章节单独详细的讲解。
下面我们简单的介绍一下Netty中的核心组件:

  • Channel
  • ByteBuf
  • EventLoop
  • Future
  • ChannelPipeline
  • ChannelHandler
    这些模块代表了不同类型的构建、资源、逻辑及通知,应用程序将使用它们来访问网络以及流经网络的数据。

Channel

基本的 I/O 操作(bind()、connect()、read()和 write())依赖于底层网络传输所提 供的原语。
在基于 Java 的网络编程中,其基本的构造是 class Socket。Netty 的 Channel 接 口所提供的 API,大大地降低了直接使用 Socket 类的复杂性。
此外,Channel 也是拥有许多预定义的、专门化实现的广泛类层次结构的根。
下面是一个简短的部分清单:

  • EmbeddedChannel — Netty专门为改进针对ChannelHandler的单元测试的一种特殊Channel实现
  • LocalServerChannel — Netty提供的用来在同一个JVM内部实现client和server之间通信的transport
  • NioDatagramChannel — Netty提供的UDP数据包的channel
  • NioSctpChannel — 异步的客户端 Sctp 连接
  • NioSocketChannel

ByteBuf

Java NIO 提供了 ByteBuffer 作为它 的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐。
Netty 的 ByteBuffer 替代品是 ByteBuf,一个强大的实现,既解决了 JDK API 的局限性, 又为网络应用程序的开发者提供了更好的 API。

EventLoop(Reactor线程组)

EventLoop 定义了 Netty 的核心抽象,用于处理连接的生命周期中所发生的事件。

  • 一个 EventLoopGroup 包含一个或者多个 EventLoop;
  • 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;
  • 所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;
  • 一个 Channel 在它的生命周期内只注册于一个 EventLoop;
  • 一个 EventLoop 可能会被分配给一个或多个 Channel。

下图为Channel、EventLoop、Thread 以及 EventLoopGroup 之间的关系。

Future

Future 提供了另一种在操作完成时通知应用程序的方式。
这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。
JDK预置了 interface java.util.concurrent.Future,但是其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。
这是非常繁琐的,所以 Netty 提供了它自己的实现 ChannelFuture,用于在执行异步操作的时候使用。
ChannelFuture提供了几种额外的方法,这些方法使得我们能够注册一个或者多个 ChannelFutureListener实例。监听器的回调方法operationComplete(),将会在对应的 操作完成时被调用。
然后监听器可以判断该操作是成功地完成了还是出错了。如果是后者,我们可以检索产生的Throwable。简而言之 ,由ChannelFutureListener提供的通知机制消除了手动检查对应的操作是否完成的必要。
每个 Netty 的出站 I/O 操作都将返回一个 ChannelFuture;也就是说,它们都不会阻塞。

ChannelPipeline

ChannelPipeline 提供了 ChannelHandler 链的容器,并定义了用于在该链上传播入站和出站事件流的API。
ChannelPipeline 它负责ChannelHandler的管理和事件拦截与调度,当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。

ChannelHandler

ChannelHandler 它充当了所有 处理入站和出站数据的应用程序逻辑的容器。类似于Servlet的Filter过滤器,负责对I/O事件或者I/O操作进行拦截和处理。
ChannelHandler 可以用于任何类型的动作,例如将数据从一种格式转换为另外一种格式,或者处理转换过程中所抛出的异常。

Choice wechat
关注公众号,获取文章更新通知。
-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!