Netty:《Scalable IO in Java》理解与实现

第一次看到这篇文章,是看到上关于wiki上关于Non-blocking-IO词条中提到了这篇文章,在后面学习Netty中大家都提到了这篇文章,就抽时间理解了一下这篇文章的内容。我个人认为改文章以Java NIO api为支持,提供了Reactor模型的实现思路。适合我们在了解简单java nio的api基础上加强与业务实现的联系。

代码传送门

个人觉得代码实现过程的收获点:


单线程中的组件

TL;DR

文章首页
论述通过分解问题,找到根本问题,解决问题的路线展开

-> 把网络服务进行拆分,细化问题
-> 传统的网络服务设计
-> 可伸缩的网络服务目标
-> 引出分治法
-> 根据分治思想引出AWT以及比AWT粒度更小的事件驱动思想
-> 引出Reactor模式
-> 简单的Reactor模式满足非阻塞的网络服务实现
-> 引入Worker Thread Pool,通过细化拆解非IO部分的任务提升性能(网络IO操作与业务的拆分)
-> 引入多Reactor线程,利用资源划分,提高Reactor的利用(网络连接与网络IO操作的细化拆分)

内容翻译

大纲

大纲
  • 伸缩式的网络服务
  • 事件驱动处理
  • Reactor反应器模式(基础版本、多线程版本、其他变种)
  • 浏览java.nio 包内的非阻塞IO api

网络服务

网络服务
  • web 服务,分布式对象,等
  • 通用的基础结构
    • 读请求
    • 解码请求
    • 处理服务
    • 编码回复
    • 发送回复
  • 不同点和开销在每一步中
    • XML解析,文件传输,web页面生成,计算服务

传统服务设计结构

传统服务设计结构
  • 每一事件在自己所属的线程中处理

传统ServerSocket 循环

每个连接在配置的线程中运行

可伸缩的指标

伸缩的指标
  • 持续增长负载下优雅降级
  • 资源消耗平缓递增
  • 可用性以及性能指标
    • 低延迟
    • 高吞吐
    • 服务质量可调节
  • 类似分治法的弹性架构目标

分治法

分治法
  • 拆分处理为小的任务;每个任务无阻塞的完成
  • 执行每个可执行的任务;经常采用触发器的形式
  • 运行被javaNio支持
    • 无阻塞的读写
    • 分发,任务与被感知到的IO时间连接
  • 多种扩展可能性
    • 面向事件驱动的体系

事件驱动

事件驱动
  • 比其他选择的区分点
    • 更少的资源依赖:不用经常分配线程给客户端
    • 更少的开销:更少的上下问切换,更少的锁操作
    • 除了事件分发会更慢:在非常多的活动与事件绑定情况下
  • 编程难点
    • 需要把处理过程必须分解为简单的无阻塞行为
      • 比GUI事件驱动粒度更小
      • 不能消除所有阻塞,GC/页缓存等
    • 必须维持服务的逻辑状态

背景介绍:AWT中的事件处理

AWT中的事件处理
  • IO事件驱动经常使用相似的思路,除了一些设计区别

Reactor Pattern

反应器模式

Reactor

通过分发给适当的处理来响应IO事件;对比AWT线程

Handlers

提供无阻塞操作;对比AWT中的动作监听器

管理与事件绑定处理器

对比:AWT中的#addActionListener()

基础的Reactor设计,单线程模式

单线程模式

java Nio的支持

Channels

支持无阻塞的读取连接文件,socket

Buffers

像数组一样可以被直接读写Channels里

Selectors

告诉那个Channels有IO事件

SelectionKeys

负责IO事件状态和绑定

Reactor 单线程实现

第一步:创建ServerSocket,等待连接

第二步:事件的分发循环

第三步:接受连接

第四步:处理器设定

第五步:请求被处理

不同状态处理器

通过attachment 绑定合适的处理器

Reactor 多线程实现

  • 有策略的增加线程为了弹性伸缩;主要适合多处理器
  • 工作线程
    • Reactors应该快速触发处理器(处理器处理会比Reactor慢)
    • 拆解非IO处理过程到其他线程中
  • 多Reactor Threads
    • Reactor线程可以充分用于IO处理
    • 多Reactor之间做负载(合理利用CPU和IO速率)

Worker Threads

  • 拆解非IO处理已提高Reactor线程的处理速度
  • Reactor线程,比POSA2 Proactor 设计的更小
  • 计算绑定处理比转换事件驱动的形式更简单【个人理解:这里表达的是如果使用Worker Threads,方便了handler的实现,如果都通过事件驱动,那么handler的实现必须依照Reactor中的要求。类似于我业务的实现要受网络连接上的处理规则限制】
    • 应该保持非阻塞计算(足够的处理消耗大于额外的开销消耗)
  • 对于IO的持续处理比较困难,最好是一开始就读取到所有的输入到一个缓冲区内
  • 使用线程池可以协调和控制,通常需要更小的线程服务更多设备

Worker Thread Pools

Worker Thread Pools

协同任务

协同任务
  • 传递:每个任务,触发,执行下一个;速度快但是容易被破坏
  • 回调:分发后的处理器回调,是GOF 调节模式的变种
  • 队列:不同阶段访问buffer
  • Future 异步通知:当每个任务产生结果,协同层顶部需要同步处理 join或者 wait/notify

使用池化执行器

使用池化执行器
  • 一个可控的工作线程池
  • 核心方法 execute(Runnable r)
  • 可以控制:
    • 任务队列类型
    • 最大、最小线程数
    • 比守护线程更柔和
    • 保活时间直到idle线程挂掉
    • 更加丰富的策略

多Reactor线程

多Reactor线程
  • 使用Reactor线程
    • 用于匹配CPU和IO速率
    • 讲台或者动态的构造器,拥有自己的选择器,线程,分发循环
    • 主接收器分配给其他Reactor

根据文章中实现的三种Reactor模式代码地址:
代码传送门

你可能感兴趣的:(Netty:《Scalable IO in Java》理解与实现)