NIO基础概念
REACTOR 模型
reactor 模型是基于事件驱动的, 它将不同的事件分配给不同的处理器执行.
Multiple reactors
- reactor 模型中的事件驱动, 是基于 i/o 多路复用机制实现的. 1 2
- selector, nio 中的多路复用器, 负责注册、处理事件.
- netty中, 基于 Multiple reactors , 即多响应器模型. 部分 reactor 只负责响应连接事件, 其它负责响应处理读写事件.
零拷贝
零拷贝技术减少了文件、socket 数据的读写过程, 这里只讨论 nio 中, 对零拷贝对使用.
linux 中的零拷贝的实现主要通过 mmap 与 sendfile 两种方式, mmap 简单说就是将内核态中的数据地址, 直接映射到用户态中, 从而用户可以跳过数据从内核态复制到用户态的过程, 而直接操作. sendfile 相较于 mmap 更进一步精简了功能, 它没有对数据地址进行映射, 通过这种方式, 只能将读取的数据存储到磁盘或soket发送出去, 性能进一步提升, 但也不能操作数据.
- nio 中, 使用直接内存实现 mmap 的零拷贝方式, MappedByteBuffer/DirectByteBuffer, DirectByteBuffer 是 MappedByteBuffer (abstract) 的子类, MappedByteBuffer 提供了它的实例方法 (直接内存不使用在JVM中申请的内存, 它只有在 full gc 时, 才会被清理. 使用直接内存, 一般会创建一个内存池, 用来重复使用).
- nio 中, FileChannel.transferTo()/transferFrom 都使用了 sendfile 的方式.
NETTY
netty 在 nio 的基础上进行了封装, 简化了 nio 复杂的处理逻辑与异常捕捉. 依据 multi reactors 模型, netty 的连接并发理论上可以做到极大(超出网络带宽的限制).
netty模版代码
public static void main(String[] args) throws InterruptedException {
EventLoopGroup master = new NioEventLoopGroup(1);
EventLoopGroup works = new NioEventLoopGroup();
try {
ServerBootstrap server = new ServerBootstrap();
server.group(master,works)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new Encode());
socketChannel.pipeline().addLast(new Decode());
socketChannel.pipeline().addLast(new ServerChannelHandler());
}
});
ChannelFuture sync = server.bind(12321).sync();
sync.channel().closeFuture().sync();
}finally {
master.shutdownGracefully();
master.shutdownGracefully();
}
}
netty 的服务代码非常简单. 下面是流程的概述:
- 首先创建两个线程池(NioEventLoopGroup, 参数为线程数量, 默认为两倍核数), 它们分别用来处理 accept 事件和 read 事件. 用来处理 accept 事件的线程池, 如果没有多个不同端口的 ServerSocketChannel 需要处理, 参数配置为 1 即可.
- 新建一个引导对象(ServerBootstrap). 将第一步创建的两个线程池作为参数传给它. option 方法用来添加网络编程的相关参数, childHandler 中添加通道处理器, 这些处理器是有方向的, read/write 分别对应实现了 channelInboundHandler/channelOutboundHandler 接口的处理器. 方向正确的处理器才会被调用. 通道中的所有处理器, 采用责任链统一处理.
- 所有参数添加完成后, 调用 server 的 bind() 方法, 这里会调用 nio 中 selector 的 select 方法, 服务开始正式运行. sync 方法, 会同步 bind 方法, 保证继续执行代码时, 绑定已经成功. closeFuture 方法是一个回调方法, 当 serverSocketChannel 关闭时, 才会被调用, 其后的 sync 同步方法, 将在它被正式调用前, 阻塞线程.
- netty 工作流程图
源码流程
- 创建 NioEventLoopGroup对象.
// nioeventLoopGroup 新建对象(无参), 最终会进入这个方法.
// nThread 是 0, executor 是 null,
// selectorProvider 是 nio 方法 SelectorProvider.provider() 创建的.
// selectStrategyFactory 是一个自定义的策略工厂 创建方法: DefaultSelectStrategyFactory.INSTANCE .
// RejectedExecutionHandlers.reject() 是线程池的拒绝策略, 它会简单的抛出异常
public NioEventLoopGroup(int nThreads, Executor executor, SelectorProvider selectorProvider, SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, new Object[]{
selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()});
}
// 当 nThreads 为 0 时, 会使用 DEFAULT_EVENT_LOOP_THREADS, 它的值等于两倍核数
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
// 多线程事件执行组对象创建方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
// 这里创建了执行器, 它主要负责创建新线程, 并对新的线程进行了一系列的复制, 添加组 等操作
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 这里根据 参数确定线程池的线程数量.每一个EventExecutor也是一个“线程池”,不过它只有一个线程
children = new EventExecutor[nThreads];
// 为每一个事件执行器实例化对象
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 类似线程池中的 worker 对象, 但有些不同. 任务队列不在group中统一维护, 而是将任务直接交给child,child中维护了一个queue,用来存储无法及时处理但任务.
// newChild 方法有多个实现, 这里是 NioEventLoopGroup 的实现. 如果确认服务器支持 epoll, 可以使用 EpollEventLoopGroup
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
if (!success) {
... // 略
}
}
}