文章

什么是 Netty ?

什么是 Netty ?

什么是Netty

Netty是一款提供异步的, 事件驱动的网络应用程序框架和工具, 用以快速开发高性能, 高可靠性的网络服务器和客户端程序.

也就是说, Netty是一个基于NIO的客户,服务器端编程框架. 使用Netty可以确保你快速和简单地开发出一个网络应用, 例如实现了某种协议的客户, 服务端应用. Netty相当简化和流线化了网络应用的编程开发过程, 例如: TCP和UDP的socket服务开发

分类Netty的特性
设计1. 统一的 API ,支持多种传输类型( 阻塞和非阻塞的 ) 2. 简单而强大的线程模型 3. 真正的无连接数据报套接字( UDP )支持 4. 连接逻辑组件( ChannelHander 中顺序处理消息 )以及组件复用( 一个 ChannelHandel 可以被多个ChannelPipeLine 复用 )
易于使用1. 详实的 Javadoc 和大量的示例集 2. 不需要超过 JDK 1.6+ 的依赖
性能拥有比 Java 的核心 API 更高的吞吐量以及更低的延迟( 得益于池化和复用 ),更低的资源消耗以及最少的内存复制
健壮性1. 不会因为慢速、快速或者超载的连接而导致 OutOfMemoryError 2. 消除在高速网络中 NIO 应用程序常见的不公平读 / 写比率
安全性完整的 SSL/TLS 以及 StartTLs 支持,可用于受限环境下,如 Applet 和 OSGI
社区驱动发布快速而且频繁

为什么选择 Netty ?

  • 使用简单:API 使用简单,开发门槛低。
  • 功能强大:预置了多种编解码功能,支持多种主流协议。
  • 定制能力强:可以通过 ChannelHandler 对通信框架进行灵活的扩展。
  • 性能高:通过与其它业界主流的 NIO 框架对比,Netty 的综合性能最优。
  • 成熟稳定:Netty 修复了已经发现的所有 JDK NIO BUG,业务开发人员不需要再为 NIO 的 BUG 而烦恼。
  • 社区活跃:版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会被加入。
  • 案例丰富:经历了大规模的商业应用考验,质量已经得到验证。在互联网、大数据、网络游戏、企业应用、电信软件等众多行业得到成功商用,证明了它可以完全满足不同行业的商业应用。

实际上,这个也是我们做技术选型的一些参考点,不仅仅适用于 Netty ,也同样适用于其他技术栈。当然,😈 面试都可以酱紫回答,显得很高端。

为什么说 Netty 使用简单?

🦅 我们假设要搭建一个 Server 服务器,使用 Java NIO 的步骤如下:

  1. 创建 ServerSocketChannel 。
    • 绑定监听端口,并配置为非阻塞模式。
  2. 创建 Selector,将之前创建的 ServerSocketChannel 注册到 Selector 上,监听 SelectionKey.OP_ACCEPT 。
    • 循环执行 Selector#select() 方法,轮询就绪的 Channel。
  3. 轮询就绪的 Channel 时,如果是处于 OP_ACCEPT 状态,说明是新的客户端接入,调用 ServerSocketChannel#accept() 方法,接收新的客户端。
    • 设置新接入的 SocketChannel 为非阻塞模式,并注册到 Selector 上,监听 OP_READ 。
  4. 如果轮询的 Channel 状态是 OP_READ ,说明有新的就绪数据包需要读取,则构造 ByteBuffer 对象,读取数据。
    • 这里,解码数据包的过程,需要我们自己编写。

🦅 使用 Netty 的步骤如下:

  1. 创建 NIO 线程组 EventLoopGroup 和 ServerBootstrap。
    • 设置 ServerBootstrap 的属性:线程组、SO_BACKLOG 选项,设置 NioServerSocketChannel 为 Channel
    • 设置业务处理 Handler 和 编解码器 Codec 。
    • 绑定端口,启动服务器程序。
  2. 在业务处理 Handler 中,处理客户端发送的数据,并给出响应。

🦅 那么相比 Java NIO,使用 Netty 开发程序,都简化了哪些步骤呢?

  1. 无需关心 OP_ACCEPT、OP_READ、OP_WRITE 等等 IO 操作,Netty 已经封装,对我们在使用是透明无感的。
  2. 使用 boss 和 worker EventLoopGroup ,Netty 直接提供多 Reactor 多线程模型。
  3. 在 Netty 中,我们看到有使用一个解码器 FixedLengthFrameDecoder,可以用于处理定长消息的问题,能够解决 TCP 粘包拆包问题,十分方便。如果使用 Java NIO ,需要我们自行实现解码器。

说说业务中 Netty 的使用场景?

  • 构建高性能、低时延的各种 Java 中间件,Netty 主要作为基础通信框架提供高性能、低时延的通信服务。例如:
    • RocketMQ ,分布式消息队列。
    • Dubbo ,服务调用框架。
    • Spring WebFlux ,基于响应式的 Web 框架。
    • HDFS ,分布式文件系统。
  • 公有或者私有协议栈的基础通信框架,例如可以基于 Netty 构建异步、高性能的 WebSocket、Protobuf 等协议的支持。
  • 各领域应用,例如大数据、游戏等,Netty 作为高性能的通信框架用于内部各模块的数据分发、传输和汇总等,实现模块之间高性能通信。

说说 Netty 如何实现高性能?

  1. 线程模型 :更加优雅的 Reactor 模式实现、灵活的线程模型、利用 EventLoop 等创新性的机制,可以非常高效地管理成百上千的 Channel 。
  2. 内存池设计 :使用池化的 Direct Buffer 等技术,在提高 IO 性能的同时,减少了对象的创建和销毁。并且,内吃吃的内部实现是用一颗二叉查找树,更好的管理内存分配情况。
  3. 内存零拷贝 :使用 Direct Buffer ,可以使用 Zero-Copy 机制。
  4. 协议支持 :提供对 Protobuf 等高性能序列化协议支持。
  5. 使用更多本地代码。例如:
    • 直接利用 JNI 调用 Open SSL 等方式,获得比 Java 内建 SSL 引擎更好的性能。
    • 利用 JNI 提供了 Native Socket Transport ,在使用 Epoll edge-triggered 的情况下,可以有一定的性能提升。
  6. 其它:
    • 利用反射等技术直接操纵 SelectionKey ,使用数组而不是 Java 容器等。
    • 实现 FastThreadLocal 类,当请求频繁时,带来更好的性能。

Zero-Copy ,在操作数据时,不需要将数据 Buffer 从一个内存区域拷贝到另一个内存区域。因为少了一次内存的拷贝,因此 CPU 的效率就得到的提升。

Netty 的高性能如何体现?

性能是设计出来的,而不是测试出来的。那么,Netty 的架构设计是如何实现高性能的呢?

  1. 线程模型 :采用异步非阻塞的 I/O 类库,基于 Reactor 模式实现,解决了传统同步阻塞 I/O 模式下服务端无法平滑处理客户端线性增长的问题。
  2. 堆外内存 :TCP 接收和发送缓冲区采用直接内存代替堆内存,避免了内存复制,提升了 I/O 读取和写入性能。
  3. 内存池设计 :支持通过内存池的方式循环利用 ByteBuf,避免了频繁创建和销毁 ByteBuf 带来的性能消耗。
  4. 参数配置 :可配置的 I/O 线程数目和 TCP 参数等,为不同用户提供定制化的调优参数,满足不同的性能场景。
  5. 队列优化 :采用环形数组缓冲区,实现无锁化并发编程,代替传统的线程安全容器或锁。
  6. 并发能力 :合理使用线程安全容器、原子类等,提升系统的并发能力。
  7. 降低锁竞争 :关键资源的使用采用单线程串行化的方式,避免多线程并发访问带来的锁竞争和额外的 CPU 资源消耗问题。
  8. 内存泄露检测 :通过引用计数器及时地释放不再被引用的对象,细粒度的内存管理降低了 GC 的频率,减少频繁 GC 带来的时延增大和 CPU 损耗。

Netty 的高可靠如何体现?

  1. 链路有效性检测:由于长连接不需要每次发送消息都创建链路,也不需要在消息完成交互时关闭链路,因此相对于短连接性能更高。为了保证长连接的链路有效性,往往需要通过心跳机制周期性地进行链路检测。使用心跳机制的原因是,避免在系统空闲时因网络闪断而断开连接,之后又遇到海量业务冲击导致消息积压无法处理。为了解决这个问题,需要周期性地对链路进行有效性检测,一旦发现问题,可以及时关闭链路,重建 TCP 连接。为了支持心跳,Netty 提供了两种链路空闲检测机制:
    • 读空闲超时机制:连续 T 周期没有消息可读时,发送心跳消息,进行链路检测。如果连续 N 个周期没有读取到心跳消息,可以主动关闭链路,重建连接。
    • 写空闲超时机制:连续 T 周期没有消息需要发送时,发送心跳消息,进行链路检测。如果连续 N 个周期没有读取对方发回的心跳消息,可以主动关闭链路,重建连接。
    • 《精尽 Netty 源码解析 —— ChannelHandler(五)之 IdleStateHandler》
  2. 内存保护机制:Netty 提供多种机制对内存进行保护,包括以下几个方面:
    • 通过对象引用计数器对 ByteBuf 进行细粒度的内存申请和释放,对非法的对象引用进行检测和保护。
    • 可设置的内存容量上限,包括 ByteBuf、线程池线程数等,避免异常请求耗光内存。
  3. 优雅停机:优雅停机功能指的是当系统推出时,JVM 通过注册的 Shutdown Hook 拦截到退出信号量,然后执行推出操作,释放相关模块的资源占用,将缓冲区的消息处理完成或清空,将待刷新的数据持久化到磁盘和数据库中,等到资源回收和缓冲区消息处理完成之后,再退出。

Netty 的可扩展如何体现?

可定制、易扩展。

  • 责任链模式 :ChannelPipeline 基于责任链模式开发,便于业务逻辑的拦截、定制和扩展。
  • 基于接口的开发 :关键的类库都提供了接口或抽象类,便于用户自定义实现。
  • 提供大量的工厂类 :通过重载这些工厂类,可以按需创建出用户需要的对象。
  • 提供大量系统参数 :供用户按需设置,增强系统的场景定制性。
本文由作者按照 CC BY 4.0 进行授权