# Netty源码解析

# ServerBootstrap与Bootstrap

  • 它们都是继承于AbstractBootstrap,分别负责服务端与客户端;
  • ServerBootstrap,服务端用于接收客户端的连接并为接收连接的用户创建Channel通道
  • BootStrap,客户端不接收连接,并且是在父通道完成系列操作。

# ServerBootstrap

ServerBootstrap负责初始化netty服务器,并且开始监听端口的socket请求。

# NioEventLoopGroup

EventLoop如同它的名字,它是一个无限循环(Loop),在循环中不断处理接收到的事件(Event)

Netty线程模型的基石是建立在EventLoop上的,从设计上来看,EventLoop采用了一种协同设计,它建立在两个基本的API之上:Concurrent和Channel,也就是并发和网络。并发是因为它采用了线程池来管理大量的任务,并且这些任务可以并发的执行。其继承了EventExecutor接口,而EventExecutor就是一个事件的执行器。另外为了与Channel的事件进行交互,EventLoop继承了EventLoopGroup接口。一个详细的EventLoop类继承层次结构如下:

**一个Netty服务端启动时,通常会有两个NioEventLoopGroup:**一个是监听线程组,主要是监听客户端请求,另一个是工作线程组,主要是处理与客户端的数据通讯。

NioEventLoopGroup bossGroup = new NioEventLoopGroup();//处理连接工作
NioEventLoopGroup workerGroup = new NioEventLoopGroup();//数据处理
1
2

Netty客户端只有一个NioEventLoopGroup,就是用来处理与服务端通信的线程组。

NioEventLoopGroup workerGroup = new NioEventLoopGroup();
1

NioEventLoopGroup可以理解为一个线程池,内部维护了一组线程,每个线程负责处理多个Channel上的事件,而一个Channel只对应于一个线程,这样可以回避多线程下的数据同步问题

  1. 对于Netty的使用,都会使用bossGroup和workerGroup的方式,而常说的bossGroup和workerGroup其实是NioEventLoopGroup的实例。
  2. 在Netty中,EventLoopGroup和NioEventLoopGroup其实就是一个线程池。

# ServerBootstrap的childHandler()与handler()的区别

ServerBootstrap的childHandler()与handler()添加的handlers是针对不同的EventLoopGroup起作用:

通过handler添加的handlers是对bossGroup线程组起作用

通过childHandler添加的handlers是对workerGroup线程组起作用

# Bootstrap的handler()

客户端Bootstrap只有handler()方法,因为客户端只需要一个事件线程组

# Channel 数据传输流

Channel,表示一个连接,可以理解为每一个请求,就是一个Channel。

ChannelHandler,核心处理业务就在这里,用于处理业务请求。

ChannelHandlerContext,用于传输业务数据。

ChannelPipeline,用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。

Netty的Channel在JDK NIO的Channel基础上做了一层封装,提供了更多的功能。

Netty的中的Channel实现类主要有:

NioServerSocketChannel(用于服务端非阻塞地接收TCP连接)

NioSocketChannel(用于维持非阻塞的TCP连接)

NioDatagramChannel(用于非阻塞地处理UDP连接)

OioServerSocketChannel(用于服务端阻塞地接收TCP连接)

OioSocketChannel(用于阻塞地接收TCP连接)

OioDatagramChannel(用于阻塞地处理UDP连接):

# ByteBuf 存储字节的容器

维护了一个字节数组

维护了两个指针,一个是读指针,一个是写指针

1)duplicate方法:复制当前对象,复制后的对象与前对象共享缓冲区,且维护自己的独立索引

2)copy方法:复制一份全新的对象,内容和缓冲区都不是共享的

3)slice方法:获取调用者的子缓冲区,且与原缓冲区共享缓冲区

# Codec 编码/解码器

HttpRequestDecoder和HttpResponseEncoder

通过Netty发送和接收一个消息的时候,就会发生一次数据转换,入站消息会被解码,也就是从字节转换为原本的形式,如果是出站消息,就会从一种形式变成字节,这个就是编码,编解码的根本原因就是因为网络数据就是一系列的字节

# write

从性能角度考虑,为了防止频繁地唤醒Selector进行消息发送,Netty的write方法只是把待发送的消息放到发送缓冲数组,再通过调用flush方法,将发送缓冲区中的消息全部写到SoceketChannel中

# sync和syncUninterruptibly

sync():等待Future直到其完成,如果这个Future失败,则抛出失败原因;

syncUninterruptibly():不会被中断的sync();