Netty源码------NioEventLoop
目录
1、初识 NioEventLoop
1.1 先来简单回顾一下Netty模型
- Netty中的Channel系列类型,对应于经典Reactor模型中的client, 封装了用户的通讯连接。
- Netty中的EventLoop系列类型,对应于经典Reactor模型中的Reactor,完成Channel的注册、轮询、分发。
- Netty中的Handler系列类型,对应于经典Reactor模型中的Handler,不过Netty中的Handler设计得更加的高级和巧妙,使用了Pipeline模式。
为了简单了解,来个对比吧:
1、单线程模型,来看下面的应用代码:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup);
2、多线程模型,再来看下面的应用代码:
EventLoopGroup bossGroup = new NioEventLoopGroup(128);
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup);
3、主从线程模型,到这里相信大家都已经想到了, 实现主从线程模型的代码如下:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup);
bossGroup 为主线程,而 workerGroup 中的线程是 CPU 核心数乘以 2,因此对应的到 Reactor 线程模型中,我们知
道, 这样设置的 NioEventLoopGroup 其实就是 Reactor 主从多线程模型。
1.2 再来了解一下NioEventLoop 和本地Selector的对应关系
NioEventLoop类型绑定了两个重要的java本地类型:一个线程类型,一个Selector类型。本地Selector属性的作用,用于注册Java本地channel。本地线程属性的作用,主要是用于轮询。
1.3 NioEventLoopGroup与NioEventLoop的关系
下面这张图其实是对Netty中NioEventLoop执行的流程图:
- 从里面可以知道,NioEventLoop就是在NioEventLoopGroup中创建的,一个NioEventLoopGroup可以包含多个NioEventLoop。
- 它会在Channel注册完之后,调用run()开始工作。详细的内容,下面介绍:
下面进入源码分析:
2、NioEventLoop创建
从我们常写的代码入手:
public void start() {
init();
//Netty封装了NIO,Reactor模型,Boss,worker
// Boss线程
EventLoopGroup bosGroup = new NioEventLoopGroup();
//worker工作线程
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//相当于Nio中的ServerSocketChannel
ServerBootstrap server = new ServerBootstrap();
//开始设置各种参数
server.group(bosGroup, workerGroup)
//主线程处理类
.channel(NioServerSocketChannel.class)
//子线程处理类Handler
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel client) throws Exception {
//这里采用了责任链模式进行
client.pipeline().addLast(new HttpResponseEncoder()) //编码器
.addLast(new HttpRequestDecoder()) //解码器
.addLast(new GPTomcatHandler()); //业务逻辑处理
}
})
.option(ChannelOption.SO_BACKLOG, 64) //针对主线程的配置,分配线程最大数量
.childOption(ChannelOption.SO_KEEPALIVE, true); //针对子线程的配置,保吃长连接
//启动服务,绑定端口,异步
ChannelFuture future = server.bind(port).sync();
System.out.println("GP Tomcat 已启动,监听的端口是:" + port);
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
bosGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
从new NioEventLoopGroup() 开始跟踪,我们会依次得到下面的代码:
public NioEventLoopGroup() {
this(0);
}
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
//线程组默认线程数为2倍的cpu数
//DEFAULT_EVENT_LOOP_THREADS=2*Runtime.getRuntime().availableProcessors()
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
#MultithreadEventExecutorGroup类,核心方法
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) {
//线程创建器,负责创建NioEventLoopGroup对应底层线程
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
children = new EventExecutor[nThreads];//创建NioEventLoop对象数组
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//for循环创建每个NioEventLoop,调用newChild(),配置NioEventLoop核心参数
children[i] = newChild(executor, args);//newChild
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) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
//线程选择器,给每个新连接分配NioEventLoop线程
chooser = chooserFactory.newChooser(children);