Netty介绍
Netty 由 Trustin Lee(韩国,Line 公司)2004 年开发的一个网络编程框架,简化了基于JDK的NIO编程,所以对于本篇文章,要求读者要了解JDK的NIO编程。Netty不仅简化了基于JDK的NIO编程,同时还具备高性能、可维护、快速开发的特性,它在JDK NIO的基础上做了极大的扩展,满足了程序员开发中常见的应用层协议,优化了一些JDK NIO本身存在的问题(本文以NIO为例)。
Netty服务器启动源码分析
由于netty本质还是基于JDK实现的,所以服务端启动主要有4个核心点(我们先围绕这四个点展开):
①初始化ServerSocket。
②将ServerSocket注册到Selector中
③绑定端口号
④监听是否有事件发生
初始化ServerBootstrap对象
我们先从ServerBootstrap实例化开始分析,在创建ServerBootstrap,我们会初始化以下参数
volatile EventLoopGroup group;
private volatile ChannelFactory<? extends C> channelFactory;
private volatile SocketAddress localAddress;
private final Map<ChannelOption<?>, Object> options = new ConcurrentHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
private volatile ChannelHandler handler;
private final Map<ChannelOption<?>, Object> childOptions = new ConcurrentHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;
上述属性值中,参与服务器启动时,主要有下面这四个属性值(其他参数暂时忽略)
volatile EventLoopGroup group;
private volatile EventLoopGroup childGroup;
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile ChannelFactory<? extends C> channelFactory;
在创建好ServerBootstrap实例对象后,我们会调用.group(bossGroup, workerGroup)
和.channel(NioServerSocketChannel.class)
这两个方法为group、childGroup、channelFactory
这三个参数赋值,其中.group(bossGroup, workerGroup)
方法为group
和childGroup
赋值,我们在调用.group(bossGroup, workerGroup)
方法时会传入两个NioEventLoopGroup
,所以此时group
和childGroup
的实际类型时NioEventLoopGroup
,而.channel(NioServerSocketChannel.class)
方法则是为channelFactory
赋值,追进channel
方法内部,可以看到有如下操作
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
channel
方法内部内部首先会调用ReflectiveChannelFactory
的有参构造,点进ReflectiveChannelFactory
类,如下
private final Constructor<? extends T> constructor;
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);
}
}
根据入参,可以得知,此时的channel方法其目的就是根据传入的NioServerSocketChannel.class,获取NioServerSocketChannel.class的无参构造器,为下文实例化NioServerSocketChannel对象做准备。
参数赋值完毕后,紧接着会执行这样一段代码 ChannelFuture f = b.bind(PORT).sync();
,此时要关注的是这个.bind(PORT)
方法,这个方法会层层调用,最终走到AbstractBootstrap
类的doBind(final SocketAddress localAddress)
方法。
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) return regFuture;
//不能肯定register完成,因为register是丢到nio event loop里面执行去了。
if (regFuture.isDone()) {
// 注册完成
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// NioServerSocket注册未注册完成
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
//等着register完成来通知再执行bind
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
Throwable cause = future.cause();
// 判断NioServerSocket注册是否发生异常
if (cause != null) promise.setFailure(cause);//将异常添加到ChannelPromise种
else {
//开始绑定端口
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
在doBind(final SocketAddress localAddress)
方法内部逻辑中,只关注两个方法initAndRegister()
和doBind0(regFuture, channel, localAddress, promise);
,其他的暂时忽略,其中doBind0(regFuture, channel, localAddress, promise);
的作用是将端口号绑定到channel
上(也就是上文中提到的③),而initAndRegister()
则负责上文提到的①②④三个点,所以我们首先关注initAndRegister()
方法,点进initAndRegister()
方法内部。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
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);
}
//开始register
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
在这段代码中,只需要关注两行代码
channel = channelFactory.newChannel();
ChannelFuture regFuture = config().group().register(channel);
首先看一下channelFactory.newChannel();
这行代码,该行代码的作用就是根据前文中初始化的无参构造器,获取到该无参构造器对应的实体类的实例对象,所以此时的channel
的实际类型是NioServerSocketChannel
。再看一下config().group().register(channel)
这行代码,先从config()
方法开始分析,该方法返回的是前文中初始化的config
对象,有兴趣的可以去看一下,在ServerBootstrap
类的第274行,所以config().group()
其实就是ServerBootstrapConfig.group()
,点进group()
方法内部。
public final EventLoopGroup group() {
return bootstrap.group();
}
根据上文中的new ServerBootstrapConfig(this)
可以知道,this
就是我们创建的ServerBootstrap
对象,那么config().group()
其实就是ServerBootstrap.group()
,而ServerBootstrap.group()
返回的就是前文中被赋值的group
属性,其实际类型是NioEventLoopGroup
,所以第二行代码的意思就是NioEventLoopGroup.register(channel)
,找到NioEventLoopGroup
类,并未找到register
方法,所以需要向上寻找,在它的父类MultithreadEventLoopGroup
中,可以找到register
方法,来看一下register
方法。
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
它首先会调用本类的next
方法,而本类的next
方法又指向父类的next
方法,而父类返回的是一个EventExecutor
对象。那么现在有一个问题。
EventExecutor
对象的实际类型是什么?
我们找到MultithreadEventLoopGroup
的父类MultithreadEventExecutorGroup
,找到这个类的next
方法。
@Override
public EventExecutor next() {
return chooser.next();
}
暂且不管chooser
的类型,接着点进next
方法,会发现这个next
方法是属于EventExecutorChooser
接口的,在这个netty框架中,这个接口只有两个实现类,先看一下它的两个实现类。
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
//executors总数必须是2的幂次方(2,4,8...等)才会用,&运算效率更高,同时当idx累加成最大值之后,相比较通用的方式(GenericEventExecutorChooser),更公平
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
//递增、取模,取正值,不然可能是负数,另外:有个非常小的缺点,当idx累加成最大值后,有短暂的不公平:
//1,2,3,4,5,6,7,0,7(注意这里不是1,而是7,然而往前的第二个也是7,所以不够公平),6,5
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
从代码中可以看出,不管是哪种实现,实例化的时候,都必须传一个EventExecutor
数组。现在又有两个问题。
这个
chooser
是在何时被实例化的?
这个EventExecutor
对象的具体类型是什么?
至此,总共有三个问题未解决
①
MultithreadEventLoopGroup
类中的next方法返回的EventExecutor
对象的实际类型是什么?
②MultithreadEventLoopGroup
类中的chooser
属性是在何时被实力化的?
③实例化chooser
属性时传入的EventExecutor
数组的具体类型是什么?
带着这三个问题,回到我们自己编写的服务器启动端代码,在编写启动端代码时,会创建的两个实例对象。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
在实例化NioEventLoopGroup
的时候,会发现,程序会执行这样一段代码
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());
}
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) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
去掉多余的代码,我们只需要关注下面这两行代码
children[i] = newChild(executor, args);
chooser = chooserFactory.newChooser(children);
会发现MultithreadEventLoopGroup
类中的chooser
属性是在初始化NioEventLoopGroup
的时候被实例化的,参数是children
,至此,问题②解决。所以现在只需要关注children
到底是什么类型的数组,只要解决了这个问题,那么问题①③都解决了。所以现在只需要关注第一行代码。newChild(executor, args)
,找到newChild
方法,会发现是一个抽象方法,查看它的具体实现。
因为我们此时实例化的是NioEventLoopGroup
对象,所以我们直接找到NioEventLoopGroup
的newChild
方法。
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}
从这段代码中可以看出,children
是一个NioEventLoop
数组,所以,所以上文中的EventExecutor
对象的实际类型是NioEventLoop
,所以next().register(channel)
其实NioEventLoop.register(channel)
,找到NioEventLoop
类,可以在它的父类SingleThreadEventLoop
找到register
,方法如下
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
上面这段代码中,只需要关注一行代码(此时上文的channel
实际类型是NioServerSocketChannel
)
promise.channel().unsafe().register(this, promise);
用逐步分析的方式,从register(new DefaultChannelPromise(channel, this))
这行代码可以看出,promise内部包裹了一个channel
,所以这行代码的实际含义是NioServerSocketChannel.unsafe().register(this, promise);
找到NioServerSocketChannel
类,可以在它的父类AbstractChannel
中找到unsafe
方法,该方法返回了一个Unsafe
对象,该对象是在实例化NioServerSocketChannel
的被实例化的,这里只贴出具体位置和类型,不做具体描述(有兴趣的可以看一下源码)。
io.netty.channel.AbstractChannel
protected AbstractChannel(Channel parent, ChannelId id) {
this.parent = parent;
this.id = id;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
io.netty.channel.nio.AbstractNioMessageChannel
@Override
protected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe();
}
所以NioServerSocketChannel.unsafe().register(this, promise)
可以简化为NioMessageUnsafe.register(this, promise)
,我们找到NioMessageUnsafe
类,在它的父类AbstractChannel.AbstractUnsafe
类中可以找到register
方法。
此时传入的
EventLoop
对象的实际类型是NioEventLoop
,而ChannelPromise
中包含一个NioServerSocketChannel
实例对象)
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
//服务器启动和客户端启动均不会走if里面的逻辑
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
这段代码我们只需要关注一个地方就行了。
至于为何只走else里面的逻辑,是本人debug得出的,至于何时会触发if里面的逻辑,本人也不是很清楚,目前并未碰到
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
根据上文梳理的结果,可以得知,此时的eventLoop
的实际类型是NioEventLoop
,所以上述代码片段可以看作NioEventLoop.execute
,传入了一个Runnable
对象,而这个Runnable
对象内部会调用一个register0
方法,我们找到NioEventLoop
类。
register0
方法是将ChannelPromise
中包含的NioServerSocketChannel
实例对象注册到selector
中
可以在NioEventLoop
类的父类SingleThreadEventExecutor
中找到execute
方法,代码如下
@Override
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
//将task任务添加到taskQueue中,等待执行
addTask(task);
if (!inEventLoop) {
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) reject = true;
} catch (UnsupportedOperationException e) {
// 空方法
}
if (reject) reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
上述代码片段只需要关注两行代码
addTask(task);
startThread();
第一行代码是将传进来的Runnable
对象添加到taskQueue
中,而第二行代码则比较关键,首先看一下startThread
方法
private void startThread() {
if (state == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
boolean success = false;
try {
doStartThread();
success = true;
} finally {
if (!success) {
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
}
}
}
}
}
这段代码并无任何复杂的逻辑,其核心是调用了doStartThread
方法,转到doStartThread
方法
private void doStartThread() {
assert thread == null;
executor.execute(() -> {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
//启动NioEventLoop中的run()方法
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
for (;;) {
int oldState = state;
if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
break;
}
}
// 检查在循环结束时是否调用了confirmShutdown()。
if (success && gracefulShutdownStartTime == 0) {
if (logger.isErrorEnabled()) {
logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " +
"be called before run() implementation terminates.");
}
}
try {
// 运行所有剩余的任务并关闭挂钩。
for (;;) {
if (confirmShutdown()) {
break;
}
}
} finally {
try {
//关闭selector
cleanup();
} finally {
//移除线程的所有fastthreadlocal。避免因为阻塞导致JVM可能会终止运行。
// See https://github.com/netty/netty/issues/6596.
FastThreadLocal.removeAll();
STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
threadLock.countDown();
if (logger.isWarnEnabled() && !taskQueue.isEmpty()) {
logger.warn("An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
terminationFuture.setSuccess(null);
}
}
}
});
}
这段代码中一行核心的代码
SingleThreadEventExecutor.this.run();
这行代码的作用便是启动NioEventLoop
类中的run
方法,找到NioEventLoop
类中的run
方法(该方法是一个死循环)。
@Override
//死循环监听、处理事件
protected void run() {
for (;;) {
try {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) selector.wakeup();
default:
}
} catch (IOException e) {
// 如果选择器发生IO异常,开始重新构建选择器
// the selector and retry. https://github.com/netty/netty/issues/8566
rebuildSelector0();
handleLoopException(e);
continue;
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// 执行所有的task任务
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
至此,已经找到了服务器启动的核心代码(NioEventLoop
类中的run
方法),这个方法首先会遍历当前NioEventLoop
的selector
,判断是否有事件发生,第二步执行processSelectedKeys
方法,判断事件类型,然后处理事件,第三步执行runAllTasks
方法,该方法的作用就是执行taskQueue
中的任务(比如之前提及的register0方法),执行完毕后移除。
下面就看一下processSelectedKeys
方法和runAllTasks
方法
processSelectedKeys方法
private void processSelectedKeys() {
if (selectedKeys != null) {
//不用JDK的selector.selectedKeys(), 性能更好(1%-2%),垃圾回收更少
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
上述代码片段并未任何复杂的逻辑,再看一下processSelectedKeysOptimized
方法
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null;
//呼应于channel的register中的this: 例如:selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
该方法便是根据事件类型,执行processSelectedKey(k, (AbstractNioChannel) a)
或者processSelectedKey(k, task)
方法,具体逻辑如下。
processSelectedKey(k, (AbstractNioChannel) a)方法
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
return;
}
try {
int readyOps = k.readyOps();
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
//处理读请求(断开连接)或接入连接
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
此处核心关注点是unsafe.read()
这行代码,NioUnsafe
类read()
只有两种具体实现,所以该行代码便是根据unsafe
类型来进行是接收请求或者读取数据。具体实现如下
io.netty.channel.nio.AbstractNioChannel.AbstractNioUnsafe(接收请求)
@Override
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
// 接收请求
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe (读取数据)
@Override
public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
//io.netty.channel.DefaultChannelConfig中设置RecvByteBufAllocator,默认AdaptiveRecvByteBufAllocator
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
//尽可能分配合适的大小:guess
byteBuf = allocHandle.allocate(allocator);
//读并且记录读了多少,如果读满了,下次continue的话就直接扩容。
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
// There is nothing left to read as we received an EOF.
readPending = false;
}
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
//pipeline上执行,业务逻辑的处理就在这个地方
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
//记录这次读事件总共读了多少数据,计算下次分配大小。
allocHandle.readComplete();
//相当于完成本次读事件的处理
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
上述代码片段中doReadMessages(readBuf)
便是用来接收请求的,具体逻辑如下
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
//接受新连接创建SocketChannel
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
processSelectedKey(k, task)方法
private static void processSelectedKey(SelectionKey k, NioTask<SelectableChannel> task) {
int state = 0;
try {
task.channelReady(k.channel(), k);
state = 1;
} catch (Exception e) {
k.cancel();
invokeChannelUnregistered(task, k, e);
state = 2;
} finally {
switch (state) {
case 0:
k.cancel();
invokeChannelUnregistered(task, k, null);
break;
case 1:
if (!k.isValid()) { // Cancelled by channelReady()
invokeChannelUnregistered(task, k, null);
}
break;
}
}
}