Netty详解

1. Netty概述

1.1 Netty简介

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步事件驱动的 网络应用程序框架 用于 快速开发 可维护的 高性能协议服务器和客户端。

Netty是目前所有NIO框架中性能最好的框架。

 

1.2 Netty特点

Netty对JDK自带的NIO的API进行了封装,解决了原生NIO(NIO类库和API复杂、需要熟悉多线程和网络编程、开发工作量和难度大、JDK NIO bug)的问题。

1.  设计

  • 适用于各种传输类型的统一 API - 阻塞和非阻塞套接字

    Netty提供了统一的API,无论你是在使用阻塞还是非阻塞套接字,都可以通过相同的编程接口进行操作。这意味着开发者不需要关心底层的网络通信细节,只需要专注于业务逻辑即可。

  • 基于灵活且可扩展的事件模型,允许明确分离关注点

    即事件发生时触发相应的处理器进行处理。这种模式允许开发者明确地分离关注点,比如将网络IO操作和业务逻辑分开,从而提高代码的清晰度和可维护性。此外,由于事件模型是可扩展的,所以你可以轻松地添加自定义的处理器来处理特定的事件。

  • 高度可定制的线程模型 - 单线程、一个或多个线程池 Netty允许开发者自由地定制线程模型,可以选择单线程或多线程方案,甚至可以使用线程池来处理并发请求。

  • 真正的无连接数据报套接字支持 Netty不仅支持传统的面向连接的TCP协议,还支持无连接的UDP协议。

2.  易用性

有据可查的 Javadoc、用户指南和示例无需额外依赖,JDK 5 (Netty 3.x) 或 6 (Netty 4.x) 就足够了

3. 性能

更高的吞吐量,更低的延迟。减少资源消耗最小化不必要的内存复制

4. 安全

完整的SSL/TLS和StartTLS支持

1.3 Netty应用场景

在分布式系统中,各个节点之间需要远程服务调用,高性能的Rpc框架必不可少。Netty作为异步高性能的通信框架,往往作为基础通信组件被使用。

2. JavaIO模型

java支持三种网络编程模式IO模式:BIO、NIO、AIO

2.1 BIO

2.1.1 工作机制

一个服务端对应一个客户端,对应一个线程

  • 服务器端启动一个ServerSocket(来监听客户端连接需求)。默认情况下服务器端需要对每一个客户建立一个线程与之通讯
  • 客户端启动socket对服务器进行通讯。
  • 客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝。
  • 如果有响应,客户端线程会等待请求结束后,在继续执行

2.1.2 问题分析

  • 每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write
  • 并发数较大时,需要创建大量线程来处理连接,系统资源占用较大
  • 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费

2.2 NIO

2.2.1 简要说明

  • NIO 有三大核心部分**: Channel(通道)、Buffer(缓冲区)、Selector(选择器)**
  • NIO 是面向缓冲区,或者面向块编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络
  • 使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
  • 通俗理解:NIO 是可以做到用一个线程来处理多个操作的。假设有 10000 个请求过来,根据实际情况,可以分配 50 或者 100 个线程来处理。不像之前的阻塞 IO 那样,非得分配 10000 个。

2.2.2 NIO和BIO的比较

  • BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多。
  • BIO 是阻塞的,NIO 则是非阻塞的。
  • BIO 基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。

2.2.3 NIO核心组件

  • 每个Thread对应一个selector选择器,一个selector选择器对应多个通道channel,每个Channel 都会对应一个 Buffer
  • 程序(Selector) 对应一个线程,一个线程对应多个连接(channel),该图反应了有三个 Channel 注册到该 Selector
  • 程序切换到哪个 Channel 是由事件决定的,Event 就是一个重要的概念
  • Selector 会根据不同的事件,在各个通道上切换。
  • Buffer 就是一个内存块,底层是有一个数组。
  • 数据的读取写入是通过 Buffer,这个和 BIO,BIO 中要么是输入流,或者是输出流,不能双向,但是 NIO 的 Buffer 是可以读也可以写,需要 flip 方法切换 Channel 是双向的,可以返回底层操作系统的情况,比如 Linux,底层的操作系统通道就是双向的

2.2.4 Selector(选择器)

1.Java 的 NIO,用非阻塞的 IO 方式。可以用一个线程,处理多个的客户端连接,就会使用到 Selector(选择器)。 2.Selector 能够检测多个注册的通道上是否有事件发生(注意:多个 Channel 以事件的方式可以注册到同一个 Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求。 3.只有在连接/通道真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程。 4.避免了多线程之间的上下文切换导致的开销。

2.2.5 Channel(通道)

1.NIO 的通道类似于流,但有些区别如下:

  • 通道可以同时进行读写,而流只能读或者只能写 通道可以实现异步读写数据 通道可以从缓冲读数据,也可以写数据到缓冲:

2.BIO 中的 Stream 是单向的,例如 FileInputStream 对象只能进行读取数据的操作,而 NIO 中的通道(Channel)是双向的,可以读操作,也可以写操作。 3.Channel 在 NIO 中是一个接口 public interface Channel extends Closeable{} 4.常用的 Channel 类有: FileChannel、DatagramChannel、ServerSocketChannel 和 SocketChannel 。**【ServerSocketChanne 类似 ServerSocket、SocketChannel 类似 Socket】**FileChannel 用于文件的数据读写,DatagramChannel 用于 UDP 的数据读写,ServerSocketChannel 和 SocketChannel 用于 TCP 的数据读写。

2.2.6 Buffer(缓冲区)

缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个容器对象(含数组),该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。Channel 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer。

2.2.7 NIO非阻塞网络编程原理分析图

2.3 NIO与零拷贝(略)

Netty详解(持续更新中)-CSDN博客

2.4 AIO(略)

Netty详解(持续更新中)-CSDN博客

3.Netty高性能架构

  • 不同的线程模式很能影响程序的性能,目前的线程模式有:
    • 传统阻塞I/O服务模型
    • Reactor模式(根据Reactor的数量处理资源池线程数量的不同,有三种典型实现)
      • 单Reactor单线程
      • 单Reactor多线程
      • 主从Reactor多线程
        • Netty线程模式 主要基于主从Reactor多线程模式做了一定改进
  • BIO、NIO分别对应了不同的IO处理方式,是底层IO机制,传统阻塞IO服务模式和Reactor模式是基于这些IO机制的具体服务设计模式。

3.1 传统阻塞IO服务模型

3.2 Reactor模式

针对传统阻塞IO服务模式两个缺点的解决方案:

  • IO复用

    多个连接共用一个阻塞对象(事件选择器Selector),应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接,当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理

    Reactor对应的叫法:

    • 反应器模式
    • 分发者模式 dispatcher
    • 通知者模式 notifier
  • 线程复用

    基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。

Reactor模式基本设计思想:IO复用结合线程池

3.2.1 Reactor模式核心组成

  • Reactor

    Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件作出反应

  • Handlers

    处理程序执行IO事件要完成的实际事件

3.2.3 单Reactor单线程

  • Select 是前面 I/O 复用模型介绍的标准网络编程 API,可以实现应用程序通过一个阻塞对象监听多路连接请求
  • Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发
  • 如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理
  • 如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应
  • Handler 会完成 Read → 业务处理 → Send 的完整业务流程

服务器端用一个线程通过多路复用搞定所有的 IO 操作(包括连接,读、写等),编码简单,清晰明了,但是如果客户端连接数量较多,将无法支撑,前面的 NIO 案例就属于这种模型。

3.2.4 单Reactor多线程

  • Reactor 对象通过 Select 监控客户端请求事件,收到事件后,通过 Dispatch 进行分发
  • 如果建立连接请求,则右 Acceptor 通过 accept 处理连接请求,然后创建一个 Handler 对象处理完成连接后的各种事件
  • 如果不是连接请求,则由 Reactor 分发调用连接对应的 handler 来处理
  • handler 只负责响应事件,不做具体的业务处理,通过 read 读取数据后,会分发给后面的 worker 线程池的某个线程处理业务
  • worker 线程池会分配独立线程完成真正的业务**,并将结果返回给 handler**
  • handler 收到响应后,通过 send 将结果返回给 client

3.2.5 主从Reactor多线程

  • Reactor 主线程 MainReactor 对象通过 select 监听连接事件,收到事件后,通过 Acceptor 处理连接事件
  • 当 Acceptor 处理连接事件后,MainReactor 将连接分配给 SubReactor
  • subreactor 将连接加入到连接队列进行监听,并创建 handler 进行各种事件处理
  • 当有新事件发生时,subreactor 就会调用对应的 handler 处理
  • handler 通过 read 读取数据,分发给后面的 worker 线程处理
  • worker 线程池分配独立的 worker 线程进行业务处理,并返回结果
  • handler 收到响应的结果后,再通过 send 将结果返回给 client
  • Reactor 主线程可以对应多个 Reactor 子线程,即 MainRecator 可以关联多个 SubReactor

3.2.6 Netty模式

  • Netty 抽象出两组线程池 BossGroup 专门负责接收客户端的连接WorkerGroup 专门负责网络的读写
  • BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup
  • NioEventLoopGroup 相当于一个事件循环组,这个组中含有多个事件循环每一个事件循环是 NioEventLoop
    • NioEventLoop 表示一个不断循环的执行处理任务线程每个 NioEventLoop 都有一个 Selector**,用于监听绑定在其上的 socket 的网络通讯**
    • NioEventLoopGroup 可以有多个线程,即可以含有多个 NioEventLoop
  • 每个 BossNioEventLoop 循环执行的步骤有 3 步
    • 轮询 accept 事件
    • 处理 accept 事件,与 client 建立连接生成 NioScocketChannel,并将其注册到某个 worker NIOEventLoop 上的 Selector
    • **处理任务队列的任务,**即 runAllTasks
  • 每个 Worker NIOEventLoop 循环执行的步骤
    • 轮询 read,write 事件
    • 处理 I/O 事件,即 read,write 事件,在对应 NioScocketChannel 处理
    • 处理任务队列的任务,即 runAllTasks
  • 每个 Worker NIOEventLoop 处理业务时,会使用 pipeline(管道),pipeline 中包含了 channel,即通过 pipeline 可以获取到对应通道,管道中维护了很多的处理器

4. Netty核心组件说明

4.1 Bootstrap、ServerBootstrap

Bootstrap 意思是引导,一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,Netty 中 Bootstrap 类是客户端程序的启动引导类,ServerBootstrap 是服务端启动引导类

//该方法用于服务器端,用来设置两个 EventLoop
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)
//该方法用于客户端,用来设置一个 EventLoop
public B group(EventLoopGroup group)
//该方法用来设置一个服务器端的通道实现
public B channel(Class<? extends C> channelClass)
//用来给接收到的通道添加配置
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value)
//用来给 ServerChannel 添加配置
public <T> B option(ChannelOption<T> option, T value)
//该方法用来设置业务处理类(自定义的handler)
public ServerBootstrap childHandler(ChannelHandler childHandler)
//该方法用于服务器端,用来设置占用的端口号
public ChannelFuture bind(int inetPort)
//该方法用于客户端,用来连接服务器端
public ChannelFuture connect(String inetHost, int inetPort)

4.2 Future、ChannelFuture

Netty 中所有的 IO 操作都是异步的,不能立刻得知消息是否被正确处理。但是可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过 Future 和 ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件。

Channel channel(),返回当前正在进行 IO 操作的通道
ChannelFuture sync(),等待异步操作执行完毕

4.3 Channel

Netty 网络通信的组件,能够用于执行网络 I/O 操作。通过 Channel 可获得当前网络连接的通道的状态;通过 Channel 可获得网络连接的配置参数(例如接收缓冲区大小)。 Channel 提供异步的网络 I/O 操作(如建立连接,读写,绑定端口),异步调用意味着任何 I/O 调用都将立即返回,并且不保证在调用结束时所请求的 I/O 操作已完成。调用立即返回一个 ChannelFuture 实例,通过注册监听器到 ChannelFuture 上,可以 I/O 操作成功、失败或取消时回调通知调用方。支持关联 I/O 操作与对应的处理程序

不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应,常用的 Channel 类型:
    NioSocketChannel,异步的客户端 TCP Socket 连接。
    NioServerSocketChannel,异步的服务器端 TCP Socket 连接。
    
    NioDatagramChannel,异步的 UDP 连接。
    
    NioSctpChannel,异步的客户端 Sctp 连接。
    NioSctpServerChannel,异步的 Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。

4.4 Selector

Netty 基于 Selector 对象实现 I/O 多路复用,通过 Selector 一个线程可以监听多个连接的 Channel 事件。 当向一个 Selector 中注册 Channel 后,Selector 内部的机制就可以自动不断地查询(Select)这些注册的 Channel 是否有已就绪的 I/O 事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个 Channel。

4.5 ChannelHandler及实现类

ChannelHandler 是一个接口处理 I/O 事件或拦截 I/O 操作,并将其转发到其 ChannelPipeline(业务处理链)中的下一个处理程序。 ChannelHandler 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类 我们经常需要自定义一个 Handler 类去继承 ChannelInboundHandlerAdapter,然后通过重写相应方法实现业务逻辑。

4.6 Pipeline和ChannelPipeLine

ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个贯穿 Netty 的链。(也可以这样理解:ChannelPipeline 是保存 ChannelHandler 的 List,用于处理或拦截 Channel 的入站事件和出站操作)ChannelPipeline 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 Channel 中各个的 ChannelHandler 如何相互交互。在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应,它们的组成关系如图。

4.7ChannelHandlerContext

保存 Channel 相关的所有上下文信息,同时**关联一个 ChannelHandler 对象。**即 ChannelHandlerContext 中包含一个具体的事件处理器 ChannelHandler,同时 ChannelHandlerContext 中也绑定了对应的 pipeline 和 Channel 的信息,方便对 ChannelHandler 进行调用。

ChannelFuture close(),关闭通道
ChannelOutboundInvoker flush(),刷新
ChannelFuture writeAndFlush(Object msg),将数据写到
ChannelPipeline 中当前 ChannelHandler 的下一个 ChannelHandler 开始处理(出站)

4.8 ChannelOption

4.9 Event Loop Group和其实现类NioEventLoopGroup

4.10 Unpooled类

5. Netty编解码器和Handler调用机制

Netty 的组件设计:Netty 的主要组件有 Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe 等。 ChannelHandler 充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现 ChannelInboundHandler 接口(或ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从 ChannelInboundHandler 冲刷数据。业务逻辑通常写在一个或者多个 ChannelInboundHandler 中。ChannelOutboundHandler 原理一样,只不过它是用来处理出站数据的 ChannelPipeline 提供了 ChannelHandler 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 pipeline 中的一系列 ChannelOutboundHandler,并被这些 Handler 处理,反之则称为入站的。

5.1 编码解码器

当 Netty 发送或者接受一个消息的时候,就将会发生一次数据转换入站消息会被解码从字节转换为另一种格式(比如 java 对象);如果是出站消息,它会被编码成字节。 Netty 提供一系列实用的编解码器,他们都实现了 ChannelInboundHadnler 或者 ChannelOutboundHandler 接口。在这些类中,channelRead 方法已经被重写了。以入站为例,对于每个从入站 Channel 读取的消息,这个方法会被调用。随后,它将调用由解码器所提供的 decode() 方法进行解码,并将已经解码的字节转发给 ChannelPipeline 中的下一个 ChannelInboundHandler。

5.2 解码器 ByteToMessageDecoder

由于不可能知道远程节点是否会一次性发送一个完整的信息,tcp 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理. 一个关于 ByteToMessageDecoder 实例分析

5.3 解码器 ReplayingDecoder

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值