Netty设计原理

Netty设计原理。

事件模型

Netty是一个基于Java NIO的高性能的通信框架。

Netty是Reactor的一种实现,通过分离Boss线程Worker线程实现高性能的IO操作。

Selector是事件驱动的核心,是基于select/epoll/kqueue等实现。

BossGroup负责不断循环执行select来获取当前已经就绪的事件。

WorkerGroup负责具体的IO操作(OP_READ/OP_WRITE等)。

注意:BossGroup中线程的数量与绑定的端口有关,如果仅绑定一个端口,线程数为1即可。

ServerBootstrapAcceptor负责ParentGroupChildGroup之间Channel的传递.

Channel

Channel是IO操作的媒介,Netty中的Channel与JDK中NIO是一一映射的,

  • NioSocketChannel <----> SocketChannel
  • NioServerSocketChannel <----> ServerSocketChannel

Channel是NIO通信的基础,selector是需要注册到Channel来能实现事件驱动的绑定。

pipeline

Netty实现了一套pipeline以支持IO流的自定义处理。

如图所示,pipeline保存了用于此Channel的所有Handler,

其中,可以被分为两种:

  • ChannelInboundHandler:处理读操作(对应着请求)的处理器
  • ChannelOutboundHandler:处理写操作(对应着回复)的处理器

channel.read()是Channel的读操作,调用findContextInbound获取Inbound处理器,

channel.forceFlush()是Channel的写入操作,调用findContextOutbound获取outbound处理器。

在使用过程中,需要注意Inbound/outbound的顺序。

那么,pipeline到底什么用,举个例子:

  • 利用ByteToMessageDecoder(ChannelInboundHandler)对读到的数据进行解码,
  • 利用MessageToByteEncoder(ChannelOutboundHandler)来对回复的数据进行编码。

selector

NioSocketChannel & NioServerSocketChannel使用SelectorProvider来获取当前平台默认的事件驱动模型。

1
SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider()

SelectionKeyselector获取到已就绪的事件,利用processSelectedKey实现读写事件的分发处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
...
// 如果是写事件
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}

// 如果是读事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
...
}

EventLoop

EventLoop是Netty中用于绑定Channel处理Channel IO的实现。

NioEventLoop继承自SingleThreadEventExecutor,也就是说,每一个NioEventLoop就是一个单线程

SingleThreadEventExecutor是一个单线程的线程池,利用队列实现任务的非阻塞的异步执行

  • 运行状态:volatile int state,会涉及到UnSafe.CAS操作
  • 任务队列:Queue<Runnable> taskQueue
  • 延迟队列:PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue

任务执行execute(Runnable task)会把当前任务添加到taskQueue,最后通过runAllTasks来触发任务的执行。

上图为NioEventLoop.run函数的大体内容,主要分为两部分,

  • 任务执行:遍历触发taskQueue的缓存的未执行的任务
  • 事件遍历:通过selector.select()获取已就绪的事件

EventLoopGroup

EventLoopGroup是用来管理EventLoop的,类似于线程池的管理。

EventLoopGroup继承自EventExecutorGroup,它实现了整个事件池的管理。

MultithreadEventLoopGroup是EventExecutorGroup的抽象实现,负责管理事件执行器的生命周期,

主要变量有:

  • 处理器的存储:EventExecutor[] children
  • 处理器的选择:EventExecutorChooserFactory.EventExecutorChooser chooser

EventExecutor是基于AbstractExecutorService的实现,等价于Reator模型中的Handler

Netty会根据chooser逻辑来选择一个事件执行器来处理Channel上的事件。

ByteBuf

ByteBuf是Netty提供的一套高性能Byte缓冲区。

读写索引

ByteBuf使用readIndexwriteIndex来记录读写情况。

ByteBuffer使用position、limit、capacity来记录读写情况,通过flip切换读写。

动态扩展

ByteBuf当内存大小不足以写入数据会触发空间扩容(ensureWritable0)。

ByteBuffer的容量固定,超出容量大小会报错。

零拷贝

CompositeByteBuf是多个ByteBuf的逻辑拼接,使用Component数组来存储ByteBuf的引用,不会产生复制。

当Component数组大小不足时候,支持自动扩容数组大小,此时的扩容也仅仅涉及到引用的复制。

总结

Netty是典型的Reactor模型,

  • Master与Worker线程分离
  • 基于线程池的Executor
  • 基于pipeline的Handler处理机制
  • ByteBuf降低缓冲区复制的性能损耗