Netty優雅退出涉及線程組,NIO線程,Channel和定時任務等,底層實現細節比較複雜。看看源碼是怎麼實現的。
NioEventLoopGroup
NioEventLoopGroup實際上是NioEventLoop線程組,它的優雅退出比較簡單,可直接遍歷EventLoop數組,循環調用它們的shutdownGracefully方法,源碼
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
private final EventExecutor[] children;
...
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
//這裏循環優雅關閉
for (EventExecutor l: children) {
l.shutdownGracefully(quietPeriod, timeout, unit);
}
return terminationFuture();
}
}
對於線程組內每個NioEventLoop執行的關閉。
NioEventLoop
NioEventLoop運行執行選擇器,在執行時候會判斷是否在關閉狀態或者已經關閉。
public final class NioEventLoop extends SingleThreadEventLoop {
@Override
protected void run() {
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
//選擇是否執行準備好
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
// 'wakenUp' 太早設置爲true:
// 1) 選擇器在wakenUp.set(false)喚醒代價比較大 // 'selector.select(...)'. (BAD)
// 2) 先判斷是否需要喚醒減少立即喚醒的損失 // 'if (wakenUp.get()) { ... }'. (OK)
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
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 {
//判斷是否已經關閉,看後邊對shutdownGracefully的分析
if (isShuttingDown()) {
//關閉註冊到selector上的所有的Channel,查看下邊對closeAll分析
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
調用NioEventLoop的shutdownGracefully方法,首先要修改線程狀態爲正在關閉狀態,它的實現在父類SingleTreadEventExecutor中,
ublic abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
private static final InternalLogger logger =
InternalLoggerFactory.getInstance(SingleThreadEventExecutor.class);
private static final int ST_NOT_STARTED = 1;
private static final int ST_STARTED = 2;
//設置正在關閉狀態
private static final int ST_SHUTTING_DOWN = 3;
private static final int ST_SHUTDOWN = 4;
private static final int ST_TERMINATED = 5;
/**
* Add a {@link Runnable} which will be executed on shutdown of this instance
*/
public void addShutdownHook(final Runnable task) {
if (inEventLoop()) {
shutdownHooks.add(task);
} else {
execute(new Runnable() {
@Override
public void run() {
shutdownHooks.add(task);
}
});
}
}
/**
* Remove a previous added {@link Runnable} as a shutdown hook
*/
public void removeShutdownHook(final Runnable task) {
if (inEventLoop()) {
shutdownHooks.remove(task);
} else {
execute(new Runnable() {
@Override
public void run() {
shutdownHooks.remove(task);
}
});
}
}
private boolean runShutdownHooks() {
boolean ran = false;
// Note shutdown hooks can add / remove shutdown hooks.
while (!shutdownHooks.isEmpty()) {
List<Runnable> copy = new ArrayList<Runnable>(shutdownHooks);
shutdownHooks.clear();
for (Runnable task: copy) {
try {
task.run();
} catch (Throwable t) {
logger.warn("Shutdown hook raised an exception.", t);
} finally {
ran = true;
}
}
}
if (ran) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
}
return ran;
}
//這裏NioEventLoop關閉
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
if (quietPeriod < 0) {
throw new IllegalArgumentException("quietPeriod: " + quietPeriod + " (expected >= 0)");
}
//判斷是否設置關閉時間
if (timeout < quietPeriod) {
throw new IllegalArgumentException(
"timeout: " + timeout + " (expected >= quietPeriod (" + quietPeriod + "))");
}
if (unit == null) {
throw new NullPointerException("unit");
}
//判斷是否是關閉狀態,單個發起時候返回
if (isShuttingDown()) {
return terminationFuture();
}
boolean inEventLoop = inEventLoop();
boolean wakeup;
int oldState;
//多個應用線程發起併發執行,netty4中使用的是循環判斷方式,也是自旋方式,也可以使用加鎖的方式
for (;;) {
if (isShuttingDown()) {
return terminationFuture();
}
int newState;
wakeup = true;
oldState = state;
if (inEventLoop) {
newState = ST_SHUTTING_DOWN;
} else {
switch (oldState) {
case ST_NOT_STARTED:
case ST_STARTED:
newState = ST_SHUTTING_DOWN;
break;
default:
newState = oldState;
wakeup = false;
}
}
//寫入新的狀態值,這裏沒有寫入繼續循環寫入,直到寫入狀態處於ST_SHUTTING_DOWN,ST_SHUTTING,ST_TERMINATED具體在isShuttingDown方法中
if (STATE_UPDATER.compareAndSet(this, oldState, newState)) {
break;
}
}
gracefulShutdownQuietPeriod = unit.toNanos(quietPeriod);
gracefulShutdownTimeout = unit.toNanos(timeout);
if (oldState == ST_NOT_STARTED) {
try {
doStartThread();
} catch (Throwable cause) {
STATE_UPDATER.set(this, ST_TERMINATED);
terminationFuture.tryFailure(cause);
if (!(cause instanceof Exception)) {
// Also rethrow as it may be an OOME for example
PlatformDependent.throwException(cause);
}
return terminationFuture;
}
}
if (wakeup) {
wakeup(inEventLoop);
}
return terminationFuture();
}
@Override
public boolean isShuttingDown() {
return state >= ST_SHUTTING_DOWN;
}
}
對closeAll實現分析,主要是把註冊在selector上的所有Channel都關閉,具體在NioEventLoop類中代碼如下:
private void closeAll() {
selectAgain();
Set<SelectionKey> keys = selector.keys();
Collection<AbstractNioChannel> channels = new ArrayList<AbstractNioChannel>(keys.size());
for (SelectionKey k: keys) {
Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
channels.add((AbstractNioChannel) a);
} else {
k.cancel();
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
invokeChannelUnregistered(task, k, null);
}
}
//將上邊迭代出來的AbstractNioChannel逐個關閉
for (AbstractNioChannel ch: channels) {
//這裏循環調用channel NioUnsafe的close方法,下面跳轉到Unsafe中,對close方法進行分析
ch.unsafe().close(ch.unsafe().voidPromise());
}
}
持續往下close方法,AbstractChannel.AbstractUnsafe.close方法
private void close(final ChannelPromise promise, final Throwable cause,
final ClosedChannelException closeCause, final boolean notify) {
if (!promise.setUncancellable()) {
return;
}
if (closeInitiated) {
if (closeFuture.isDone()) {
// Closed already.
safeSetSuccess(promise);
} else if (!(promise instanceof VoidChannelPromise)) { // Only needed if no VoidChannelPromise.
// This means close() was called before so we just register a listener and return
closeFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
promise.setSuccess();
}
});
}
return;
}
closeInitiated = true;
final boolean wasActive = isActive();
final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
//發送隊列清空,不允許添加新的消息,刷新輸出
this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer.
Executor closeExecutor = prepareToClose();
if (closeExecutor != null) {
closeExecutor.execute(new Runnable() {
@Override
public void run() {
try {
// Execute the close.調用NioSocketChannel.doClose
doClose0(promise);
} finally {
// Call invokeLater so closeAndDeregister is executed in the EventLoop again!
invokeLater(new Runnable() {
@Override
public void run() {
if (outboundBuffer != null) {
// Fail all the queued messages
outboundBuffer.failFlushed(cause, notify);
outboundBuffer.close(closeCause);
}
fireChannelInactiveAndDeregister(wasActive);
}
});
}
}
});
} else {
try {
// Close the channel and fail the queued messages in all cases.
doClose0(promise);
} finally {
if (outboundBuffer != null) {
// Fail all the queued messages.
outboundBuffer.failFlushed(cause, notify);
outboundBuffer.close(closeCause);
}
}
//close前判斷是否有消息正在發送,如果有則將SelectionKey的註冊操作封裝成Task放到eventLoop中稍後再執行
if (inFlush0) {
//稍後執行,先放入 eventLoop().execute(task);
invokeLater(new Runnable() {
@Override
public void run() {
fireChannelInactiveAndDeregister(wasActive);
}
});
} else {
fireChannelInactiveAndDeregister(wasActive);
}
}
}
//調用pipeline的fireChannelInactive,觸發鏈路關閉通知事件。
private void fireChannelInactiveAndDeregister(final boolean wasActive) {
deregister(voidPromise(), wasActive && !isActive());
}
執行完資源釋放和連接關閉後,通過上邊的分析可以梳理下close方法過程。調用過程。
- 是否有消息正在發送,將新來的註冊操作掛起,包裝成eventloop稍後執行,
- 將發送隊列清空,不再允許發送新消息。
- 調用NioSocketChannel的doClose方法,關閉鏈路。javaChannel().close();
- 調用pipline的fireChannelInactive觸發鏈路關閉通知事件
- 調用AbstractNioChannel的Deregister,從多路複用上取消selectionKey
- 調用ChannelOutboundBuffer的close方法,釋放發送隊列中所有未完成發送的ByteBuf,等待GC