Bootstrap

Java Netty:基础入门(一)

Netty:异步的,基于事件驱动的网络应用框架,快速开发高性能的服务端和客户端

一、Netty 简介

  • 简介:异步的,基于事件驱动的网络应用框架,快速开发高性能的服务端和客户端
    • Core
      • Zero-Copy-Capable Rich Byte Buffer:零拷贝
      • Universal Communication API:交互 API
      • Extensible Event Model:可扩展事件模型
    • Protocol Support
      • HTTP & WebSocket、SSL-StartTLS、Google Protobuf、zlib/gzip、Large File Transfer、RTSP…
    • Transport Services
      • Socket & Datagram
      • HTTP Tunnel
      • In-VM Pipe
  • 官网说明
    • 由 JBOSS 提供的一个 Java 开源框架,Netty 提供异步的、基于事件驱动的网络应用程序框架,用于快速开发高性能、高可靠的网络 IO 程序
    • Netty 可以帮助你快速、简单的开发出一个网络应用,相当于简化和流程化了 NIO 的开发过程
    • Netty 是目前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得广泛的应用
    • Elasticsearch、Dubbo 框架内部都采用 Netty
  • 优点
    • 涉及优雅:适用各种传输类型的统一 API 阻塞和非阻塞 Socket
    • 使用方便:没有其他依赖项,JDK 5 / 6 就足够了
    • 高性能、吞吐量更高、延迟更低、减少资源消耗、最小化不必要的内存复制
    • 安全:完整的 SSL/TLS 和 StartTLS 支持
    • 社区活跃…
  • 版本说明
    • 3.x、4.x、5.x
      • 5.x 出现重大 Bug 被废弃,常用 Netty4.x

二、线程模型

2.1 传统阻塞 I/O 服务模型

  • 特点
    • 采用阻塞 IO 模式获取输入数据
    • 每个连接都需要独立的线程完成数据输入、业务处理、数据返回
    • 问题
      • 并发数很大,就会创建大量的线程,占用很大系统资源
      • 连接创建后,当前线程如果没有数据可读,该线程会阻塞在 read 操作,造成线程资源浪费

2.2 Reactor 模式

  • 解决 传统 IO 问题、基于 IO 复用模型、基于线程池复用线程资源
  • 单 Reactor 单线程
    • 方案
      • Select 可以实现应用程序通过一个阻塞对象监听多路连接请求
      • Reactor 对象通过 Select 监控客户端请求事件,收到事件后进行 Dispatch 分发
      • 建立的是连接请求,由 Acceptor 通过 Accept 处理连接请求并创建一个 Handler 对象处理连接完成后的后续业务
      • 不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应
      • Handler 会完成 Read -> 业务处理 -> Send
    • 优点:模型简单,没有多线程、进程通信、竞争问题全在一个线程完成
    • 缺点:性能问题,只有一个线程,无法发挥 CPU 性能,很容易导致性能瓶颈,可靠性问题
    • 使用场景:客户端有限,业务处理迅速
  • 单 Reactor 多线程
    • 方案
      • Reactor 对象通过 select 监控客户请求事件,收到后通过 dispatch 进行分发
      • 如果建立连接请求,由 Acceptor 通过 accept 进行处理,建立一个 Handler 对象处理完成各种事件
      • 不是连接请求,由 reactor 分发对应的 handler
      • handler 只负责响应,不做具体业务处理,通过 read 读取数据后,分发给后面的 worker 线程池的某个线程处理
      • worker 线程池分配独立线程完成真正的业务,并返回结果给 handler
      • handler 收到响应后,通过 send 将结果返回 client
    • 优点:充分利用 CPU 多核的处理能力
    • 缺点:多线程数据共享和访问比较复杂,reactor 处理所有事件的监听和响应,单线程运行很容易出现性能瓶颈
  • 主从 Reactor 多线程
    • 方案
      • Reactor 主线程 MainReactor 对象通过 select 监听连接事件,收到后通过 Acceptor 处理连接事件
      • 处理连接事件后,MainReactor 将连接分配给 SubReactor
      • SubReactor 将连接加入到连接队列进行监听,并创建 handler 进行事件处理
      • handler 通过 read 读取数据,分发给后面的 worker 线程处理
      • worker 线程池分配独立的 worker 线程进行业务处理,并返回结果
      • handler 收到响应的结果后,再通过 send 将结果返回给 client
    • 优点:父线程和子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理
    • 缺点:编程复杂度高
  • 说明
    • Reactor 模式,通过一个或多个输入同时传递给服务处理器的模式(基于事件驱动)
    • 服务器端程序处理传入的多个请求,并将他们同步分派到相应的处理线程,因此 Reactor 模式也叫 Dispatcher 模式
    • Reactor 模式使用 IO 复用监听事件,收到事件后,分发给某个线程(进程),这就是网络高并发处理关键
  • 核心组成
    • Reactor:在一个单独的线程运行,复制监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应
    • Handlers:处理程序执行 IO 事件要完成的实际事件,Reactor 通过调度适当的处理程序来响应 IO 事件,处理程序执行非阻塞操作
    • 优点:响应快、最大程度避免复杂的多线程及同步问题、扩展性好、复用性好

三、Netty 模型

  • 简单版方案
    • BossGroup 线程维护 Selector,只关注 Accept
    • 接收到 Accept 事件,获取对应的 SocketChannel 封装成 NIOSocketChannel 并注册到 Worker 线程(事件循环)中进行维护
    • 当 Worker 线程监听到 selector 中有感兴趣到事件后,就进行处理(由 handler),注意 handler 以及加入到通道
  • 详细版方案
    • Netty 抽象出两组线程池 BossGroup 专门负责接收客户端的连接,WorkerGroup 专门负责网络的读写
    • BossGroup 和 WorkerGroup 类型都是 NIOEventLoopGroup
    • NIOEventLoopGroup 相当于一个事件循环组,这个组中包含多个事件循环,每一个事件循环是 NIOEventLoop
    • NIOEventLoop 表示一个不断循环的执行处理任务的线程,每个 NIOEventLoop 都有一个 selector,用于监听绑定在其上的 socket 的网络通讯
    • NIOEventLoopGroup 可以有多个线程,可以含有多个 NIOEventLoop
    • 每个 Boss NIOEventLoop 循环执行的步骤有三步
      • 轮询 accept 事件
      • 处理 accept 事件,与 client 建立连接,生成 NIOSocketChannel,并注册到某个 worker NIOEventLoop 上的 selector
      • 处理任务队列的任务,即 runAllTasks
    • 每个 Worker NIOEventLoop 循环执行的步骤
      • 轮询 read、write 事件
      • 处理 IO 事件,即 read、write 事件,在对应的 NIOSocketChannel 处理
      • 处理任务队列的任务,即 runAllTasks
    • 每个 Worker NIOEventLoop 处理业务时,会使用 pipeline,包含了 channel,即通过 pipeline 可以获取到对应的管道,维护了很多处理器
  • 任务队列中的 Task 使用场景
    • 用户自定义的普通任务
    • 用户自定义的定时任务
    • 非当前 Reactor 线程调用 Channel 的各种方法
  • 方案再说明
    • Netty 抽象出两组线程池,BossGroup 专门负责接收客户端连接,WorkerGroup 专门负责网络读写操作
    • NioEventLoop 表示一个不断循环执行任务处理的线程,每个 NioEventLoop 都有一个 selector,用于监听绑定在其上的 socket 网络通道
    • NioEventLoop 内部采用串行化设计,从消息的读取 -> 解码 -> 编码 -> 发送,始终由 IO 线程 NioEventLoop 负责
      • NioEventLoopGroup 下包含多个 NioEventLoop
      • 每个 NioEventLoop 中包含有一个 Selector,一个 taskQueue
      • 每个 NioEventLoop 的 Selector 上可以注册监听多个 NioChannel
      • 每个 NioChannel 之后绑定在唯一的 NioEventLoop 上
      • 每个 NioChannel 都绑定有一个自己的 ChannelPipeline
  • 异步模型
    • 基本介绍
      • 异步过程调用发出后,不能立刻得到结果,实际处理在完成后,通过状态、通知和回调来通知调用者
      • Netty 中的 IO 操作是异步的,包括 Bind、Write、Connect 等操作会简单返回一个 ChannelFuture
      • 调用者不能立刻得到结果,需要通过 Future-Listener 机制,用户可以方便的主动获取或者通知机制获取 IO 操作结果
      • 建立在 future 和 callback 之上
      • future 核心思想:一个方法计算过程可能非常耗时,调用的时候会返回一个 Future,通过 Future 去监控方法处理过程
    • Future 说明
      • 表示异步的执行结果,可以通过提供的方法来检测执行是否完成,比如检索计算等
      • ChannelFuture 是一个接口,可以添加监听器,当监听的事件发生时,就会通知监听器
    • Future-Listener 机制
      • Future 对象刚创建时,处于非完成状态,可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作
      • 常见操作
        • isDone:判断是否完成
        • isSuccess:判断是否成功
        • getCause:获取当前操作失败原因
        • isCancelled:是否被取消
        • addListener:注册监听器,操作完成会通知指定的监听器,Future 对象已完成,则会通知指定的监听器
  • 小结:相比传统阻塞 IO,异步处理不会造成线程阻塞,操作期间可以执行其他程序,高并发下会更稳定和更高的吞吐量
;