Netty Server 源码分析
1、EventLoopGroup
EventLoopGroup 最终继承ScheduledExecutorService
,这个是 java 的线程池,所以他的本质就是一个线程池,可以让你从中获取到新的线程并负责线程生命周期的管理。
1.1、NioEventLoopGroup
NioEventLoopGroup 在初始化时会默认传递一个0进入方法,这个0代表的最初的线程数量,并会设置一个空的 Executor 和 SelectorProvider.provider() 这个会获取到自己的 Selector。
//1 初始化
public NioEventLoopGroup() {
this(0);
}
// 2 初始化
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
// 3 初始化 selector
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
// 4 对默认值进行处理
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
在后续中对默认值进行处理,线程数据如果为零的话,会从环境变量中获取 io.netty.eventLoopThreads 的值,如果获取不到话会用当前可用CPU核数*2来作为线程数量的值,当前这里有一个最小限制为1
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
最终会走入下面方法,首先会对 executor 进行默认线程工厂的赋值,然后会有一个 EventExecutor 的数组,用来存放线程
/**
* Create a new instance.
*
* @param nThreads the number of threads that will be used by this instance.
* @param executor the Executor to use, or {@code null} if the default should be used.
* @param args arguments which will passed to each {@link #newChild(Executor, Object...)} call
*/
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 这一行是核心处理逻辑
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
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) {
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) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
。。。 。。。
}
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0]);
}
那么 NioLoopGroup 线程池管理的线程就是 NioEventLoop,每个 NioEventLoop 都会负责一部分客户端的 SockerChannel 因为在初始化的时候都会有自己的一个 Selector,所有这些 SocketChannel 都会注册到自己的 Selector 上去,这样每个 NioEventLoop 就会通过自己的 Selector 进行轮询处理客户端请求。
💡 整个 NioEventLoopGroup 就是一个线程池,线程数据为 CPU 核心数 * 2,最小为1。每个线程对象是 NioEventLoop ,每个线程有自己 SockerChannel 并将其注册到自己的 Selector上去进行轮询请求,将请求传给 Handler 进行请求处理。2、ServerBootstrap
ServerBootstrap serverBootstrap =newServerBootstrap();
serverBootstrap
.group(parentGroup, childGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_BACKLOG, 1024)
.childHandler(newChannelInitializer<SocketChannel>() {//创建通道初始化对象
//给 pipeline设置处置器
protected voidinitChannel(SocketChannel ch)throwsException {
//传入自定义的 handler
ch.pipeline().addLast(newNettyServerHandler());
}
});
//启动服务器并绑定端口
ChannelFuture cf = serverBootstrap.bind(6666).sync();
2.1 bind
- ServerSocketChannel 对象的创建
// 用来初始化 ServerSocketChannel
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 创建 ServerSocketChannel 并监听 OP_ACCEPT 事件
channel = channelFactory.newChannel();
// 初始化我们设置的相关参数
// 增加一个内置的 pipeline
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
核心是通过 channelFactory 来进行channel的创建,在我们构建 ServerBootStarp 的时候设置了 channel(NioServerSocketChannel.class) 属性,默认构建了 ReflectiveChannelFactory
// 设置参数时传递
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
也就是说在进行 newChannel() 调用的时候实际是走的 ReflectiveChannelFactory ,对我们设置的 NioServerSocketChannel 类通过反射构建 ServerSocketChannel 实例并让其关注 OP_ACCEPT 事件。
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
// 获取默认的 Selector
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
// 最终通过对 nio 底层的调用,获取 ServerSocketChannel 对象
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a server socket.", e);
}
}
/**
* Create a new instance
*/
public NioServerSocketChannel() {
// 这里通过反射用无参构造器进行对象的创建
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
// 同时还会绑定 OP_ACCEPT 事件监听连接请求
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
这个文档不好写,以上的话标题暂时不动了,后续的笔记不太适合大规模的代码的贴放,所以后续的笔记的话,纯采用文字+流程图的形式展示
3、连接事件
因为 NioEventLoop 是一个线程且继承自线程组不过只是有一个线程的线程组,所以他的核心逻辑是在 run() 方法中,除了处理调用 submit() 方法到队列中的任务外,就是调用自身 Selector#select() 方法进行事件的监听, 当获取到连接事件后,会在 doReadMessage() 中调用 nio 底层的 accept() 方法完成连接请求的处理并返回一个 socketChannel ,我们在启动 Netty Server 的时候设置了两个线程组 parent 和 child ,那么此时会将完成连接请求的 channel 注册到 childGroup 中的 Selector 上监听后续读写事件。
4、读写事件
-
Read 事件
当连接请求处理完毕后会将获取到的 SocketChannel 注册到 childGroup 中的某个线程的 Selector 上面,然后 childGroup 中的线程对自身的 Selector 进行轮询,当获取到 read 事件后,会将消息封装到 ByteBuf 中,所有我们自己写的 Handle 中都会对消息进行强转为 ByteBuf ,封装完毕后最终会根据 Piepline 找到我们自己实现的 Handle 去实现相对应的 read 事件的逻辑。
-
Write事件
当我们收到 Read 事件 后会发送一个响应信息,会将响应消息放到一个 outBondMessage 缓冲中 最后会到我们自己实现的 Handle#channelReadComplete 调用 flush 方法,这个方法的话会调用底层 Nio 的write