Netty源码-EventLoop分析
一、EventLoop作用
核心作用:IO操作(accept、read、Write),普通任务、定时任务
组成:EventLoop中封装了Selector、Thread、任务队列
二、EventLoop的类关系图
在NioEventLoop中有三个属性
private Selector selector;
private Selector unwrappedSelector;
private SelectedSelectionKeySet selectedKeys;
在 SingleThreadEventExecutor中封装了队列和线程
private final Queue<Runnable> taskQueue;
private volatile Thread thread;
属性标志着这个类拥有什么功能,因此从他的属性可以知道这个类拥有什么功能,说明EventLoop这个类是对Thread、Queue、Thread的封装。
问题:为什么会在NioEventLoop中提供两个Selector属性,分别为selector和unwrappedSelector为什么?
select是对unWrappedSelector的进一步封装,原始的unWrappedSelector的unWrappedSelector是采用set()集合存储的,这样查询的效率相对较低,而Nettyd对Select进行了重构,采用了List几个进行存储,List的遍历速度相对于Set要快特别多。
三、Selector的创建时机
Selector的创建是在NioEventLoop()的构造方法中创建的
NioEventLoop.openSelector()方法
private SelectorTuple openSelector() {
final Selector unwrappedSelector;
try {
//1、创建Selector,这个创建方式和原始Nio中Selector的创建方式是一样的
unwrappedSelector = provider.openSelector();
} catch (IOException e) {
throw new ChannelException("failed to open a new selector", e);
}
if (DISABLE_KEY_SET_OPTIMIZATION) {
return new SelectorTuple(unwrappedSelector);
}
Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
return Class.forName(
"sun.nio.ch.SelectorImpl",
false,
PlatformDependent.getSystemClassLoader());
} catch (Throwable cause) {
return cause;
}
}
});
if (!(maybeSelectorImplClass instanceof Class) ||
// ensure the current selector implementation is what we can instrument.
!((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
if (maybeSelectorImplClass instanceof Throwable) {
Throwable t = (Throwable) maybeSelectorImplClass;
logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
}
return new SelectorTuple(unwrappedSelector);
}
final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
if (PlatformDependent.javaVersion() >= 9 && PlatformDependent.hasUnsafe()) {
// Let us try to use sun.misc.Unsafe to replace the SelectionKeySet.
// This allows us to also do this in Java9+ without any extra flags.
long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField);
long publicSelectedKeysFieldOffset =
PlatformDependent.objectFieldOffset(publicSelectedKeysField);
if (selectedKeysFieldOffset != -1 && publicSelectedKeysFieldOffset != -1) {
PlatformDependent.putObject(
unwrappedSelector, selectedKeysFieldOffset, selectedKeySet);
PlatformDependent.putObject(
unwrappedSelector, publicSelectedKeysFieldOffset, selectedKeySet);
return null;
}
// We could not retrieve the offset, lets try reflection as last-resort.
}
//2、采用反射的方式将原始Selector中的selectedKeys、publicSelectedKeys设置到新创建的Selector中
Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
if (cause != null) {
return cause;
}
cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
if (cause != null) {
return cause;
}
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
return null;
} catch (NoSuchFieldException e) {
return e;
} catch (IllegalAccessException e) {
return e;
}
}
});
if (maybeException instanceof Exception) {
selectedKeys = null;
Exception e = (Exception) maybeException;
logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
return new SelectorTuple(unwrappedSelector);
}
selectedKeys = selectedKeySet;
logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
return new SelectorTuple(unwrappedSelector,
new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}
这个方法首先采用原始Nio的方式将Selector进行创建出来,创建完成之后,采用反射的方式将selectedKeys、publicSelectedKeys设置到新创建的Selector中。
四、EventLoop处理任务
EventLoop主要可以处理IO任务、普通任务、定时任务
EventLoop处理普通任务
SingleThreadEventExecutor.execute()
private void execute(Runnable task, boolean immediate) {
//1、判断当前线程是否为NIO线程
boolean inEventLoop = inEventLoop();
//2、将任务添加到任务队列里面
addTask(task);
if (!inEventLoop) {
//3、不是NIO线程开启一个新的线程
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
}
if (reject) {
reject();
}
}
}
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);
}
}
startThread()
private void startThread() {
//1、state的初始化值为1 ST_NOT_STARTED的初始化值为1
if (state == ST_NOT_STARTED) {
//2、将state的值增加一,然后开始创建新的线程,表示只会创建一次NIO线程
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()创建线程
private void doStartThread() {
assert thread == null;
//1、创建一个Nio线程
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
//2、通过EventLoop线程执行IO操作,普通任务 定时任务
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;
}
}
// Check if confirmShutdown() was called at the end of the loop.
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;
}
}
for (;;) {
int oldState = state;
if (oldState >= ST_SHUTDOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTDOWN)) {
break;
}
}
confirmShutdown();
} finally {
try {
cleanup();
} finally {
FastThreadLocal.removeAll();
STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
threadLock.countDown();
int numUserTasks = drainTasks();
if (numUserTasks > 0 && logger.isWarnEnabled()) {
logger.warn("An event executor terminated with " +
"non-empty task queue (" + numUserTasks + ')');
}
terminationFuture.setSuccess(null);
}
}
}
}
});
}
SingleThreadEventExecutor.this.run();
protected void run() {
int selectCnt = 0;
for (;;) {
try {
int strategy;
try {
//1、这是一个策略,判断普通任务队列里面是否有任务,有个有普通任务调用slectNow()非阻塞
//的方式运行,如果没有则返回SelectStrategy.SELECT
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
//2、以上两个case一般不会走,会走这个的表示,普通任务队列为空,那么这个任务就可能为
//定时任务或者IO任务
case SelectStrategy.SELECT:
//获取定时任务的延迟时间,如果没有则返回-1
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
//如果返回-1则表示没有,设置curDeadlineNanos 为Long.MAX_VALUE
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
//能走到这表示普通任务队列肯定是为空的,那么hasTasks()返回false,
//则!hasTasks()一定为True,则一定会走这个方法
if (!hasTasks()) {
//没有定时任务则阻塞,如果有定时任务,延时到定时任务开始时唤醒
strategy = select(curDeadlineNanos);
}
} finally {
// This update is just to help block unnecessary selector wakeups
// so use of lazySet is ok (no race condition)
nextWakeupNanos.lazySet(AWAKE);
}
// fall through
default:
}
} catch (IOException e) {
// If we receive an IOException here its because the Selector is messed up. Let's rebuild
// the selector and retry. https://github.com/netty/netty/issues/8566
rebuildSelector0();
selectCnt = 0;
handleLoopException(e);
continue;
}
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
//IORatio IO比率为50
final int ioRatio = this.ioRatio;
boolean ranTasks;
//ioRatio 默认为50,这里一般不会走
if (ioRatio == 100) {
try {
if (strategy > 0) {
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
//重要:处理IO任务
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
//处理普通任务
//注意: IO操作一定要都完成普通任务操作到了时间结束,即使没有做完,也结束
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
if (ranTasks || strategy > 0) {
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} catch (CancelledKeyException e) {
// Harmless exception - log anyway
if (logger.isDebugEnabled()) {
logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
selector, e);
}
} 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);
}
}
}
processSelectedKeys()处理IO任务
遍历SelectedKeys并且执行processSelectedKey()
processSelectedKey()
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
//ch为ServerSocketChannel或者SocketChannel
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop == this) {
unsafe.close(unsafe.voidPromise());
}
return;
}
try {
int readyOps = k.readyOps();
//1、处理OP_CONNECT事件
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
//2、处理OP_WRITE事件
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
//3、处理OP_ACCEPT或者OP_READ 事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
站在ServerSocketChannel角度,发现Accept事件之后要进行
//1、获取SocketChannel
SocketChannel sc = channel.accept();
//2、设置非阻塞
sc.configureBlocking(false);
//3、selector监听
SelectionKey selectionKey = sc.register(selector, 0, null);
//4、设置监听事件
selectionKey.interestOps(SelectionKey.OP_READ);
五、Netty怎么处理IO Read操作
1、服务端代码
public class NettyServer {
private static final Logger log = LoggerFactory.getLogger(NettyServer.class);
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.channel(NioServerSocketChannel.class);
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
// serverBootstrap.childOption()
final LoggingHandler loggingHandler = new LoggingHandler();
try {
serverBootstrap.group(bossGroup, workerGroup);
//设置IO ByteBuffer大小
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler());
}
});
Channel channel = serverBootstrap.bind(8080).sync().channel();
channel.closeFuture().sync();
} catch (InterruptedException e) {
log.debug("server error is {}", e);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2、客户端代码
public class NettyClient {
private static final Logger log = LoggerFactory.getLogger(NettyClient.class);
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
NioEventLoopGroup group = new NioEventLoopGroup();
try {
bootstrap.group(group);
bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(final NioSocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LoggingHandler());
pipeline.addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buffer = ctx.alloc().buffer();
buffer.writeCharSequence("hello,hezhenbin", Charset.defaultCharset());
ctx.writeAndFlush(buffer);
}
});
};
});
Channel channel = bootstrap.connect(new InetSocketAddress(8080)).sync().channel();
channel.closeFuture().sync();
} catch (InterruptedException e) {
log.debug("client error is {}", e);
} finally {
group.shutdownGracefully();
}
}
}
3、打断点在监听事务的方法
4、第一次处理的是accept的事件,然后处理AbstractNioMessageChannel.read()
@Override
public void read() {
assert eventLoop().inEventLoop();
//获取配置
final ChannelConfig config = config();
//获取pipeline
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
//1、封装成NioSocketChannel
//2、将SocketChannel存储到NioSocketChannel
//3、将原始的SocketChannel设置为非阻塞
//4、将ServerSocketCHannel设置成为parent
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;
}
//readBuf保存的是NioSocketChannel
int size = readBuf.size();
//遍历所有的NioSocketChannel
for (int i = 0; i < size; i ++) {
readPending = false;
//调用NioSocketChannel中pipeline的ChannelRead()方法,
//在accept阶段的时候,这时候pipline中主要有三个Handler
//主要是Head、ServerBootstrapAcceptor()、和Tail,因此调用的顺序为
//head---->ServerBootstrapAcceptor------>Tail
//因为HeadContext中的ChannelRead只有 ctx.fireChannelRead(msg);
//因此直接传到下一个Handler为ServerBootstrapAcceptor
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 {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
在 ServerBootstrap.channelRead()中打上debug,上面放行过了 pipeline.fireChannelRead(readBuf.get(i));
childGroup.register(child),EventLoop开启一个新线程进行注册,调用AbstractChannel.register()
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "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 (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
//EventLoop开启一个新的线程执行注册操作
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
执行AbstractChannel中的register0方法
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
//注册SocketChannel ChannelInitializer下的initChannel方法
doRegister();
neverRegistered = false;
registered = true;
//回调我们在服务端中写的
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
//调用pipline中的所有ChannelActive()方法进行事件的注册,主要在HeadContext中的
//ChannelActive()方法中,在HeadContext下打下断点
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
HeadContext.ChannelActive操作
AbstractNioChannel.doBeginRead()进行Read事件注册
至此完成SocketChannel中Read事件的监听。
当监听到Read事件之后
NioEventLoop.read()
AbstractNioByteChannel.read()
@Override
public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
//1、创建ByteBuffer用于接收客户端写的数据
byteBuf = allocHandle.allocate(allocator);
//2、doReadBytes(byteBuf)这个方法执行完成后,ByteBuffer就存储了
//对应数据
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
readPending = false;
}
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
//3、触发所有handler中的ChannelRead方法
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
allocHandle.readComplete();
//4、触发所有handler中的ChannelReadComplete()方法
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
1、为什么在读取客户端传输过来的数据,要用do while循环进行读取的?
为了防止ByteBuf的大小,不能一次性将客户端的数据都读完
这个pipeline.fireChannelRead(byteBuf);在循环体内部,会被多次调用,那么是不是等同于客户端发送了多次数据。
不是
3、doReadBytes(byteBuf)读数据,将数据读到ByteBuffer
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
//1、获取ByteBuffer的分配器
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
//2、试图把ByteBuf中的可用空间全占满,尝试的目的是为了动态的调整ByteBuffer,
//将ByteBuffer的可写量保存一份
allocHandle.attemptedBytesRead(byteBuf.writableBytes());
//3、通过java原生的SocketChnnel往ByteBuffer中写数据,写了多少数据
//作为doReadBytes()的返回值
return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
}
4、这个allocHandle.continueReading()什么时候结束?
private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
@Override
public boolean get() {
//lastBytesRead表示这次实际读到的数据
//attemptedBytesRead 表示ByteBuffer的可写容量
//true 当前这个读到的数据,把ByteBuffer写满了
//false 当前这个读到的数据,ByteBuffer没有写满
return attemptedBytesRead == lastBytesRead;
}
};
@Override
public boolean continueReading() {
return continueReading(defaultMaybeMoreSupplier);
}
private final boolean respectMaybeMoreData = DefaultMaxMessagesRecvByteBufAllocator.this.respectMaybeMoreData;
private volatile boolean respectMaybeMoreData = true;
@Override
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
//config.isAutoRead()默认为true
//respectMaybeMoreData默认也为true 则!respectMaybeMoreData为false
//maybeMoreDataSupplier.get()调用上面的get()方法,表示查看ByteBuffer
//是否写满
//totalMessages表示这个循环的次数
//maxMessagePerRead 16
//totalMessages < maxMessagePerRead表示最多这个循环执行16次
//16次如果没有完成就退出了,但是selector继续监听read 可以下一次再搞
return config.isAutoRead() &&
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
totalMessages < maxMessagePerRead &&
totalBytesRead > 0;
}