NioByteUnsafe封裝了NioSocketChannel讀取底層數據的流程。
NioEventLoop負責監聽Selector上所有的事件,當發生事件時根據事件類型調用Channel的UnSafe中的方法去處理。
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) {
// 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());
}
}
NioByteUnsafe當中的read方法
1、分配ByteBuf。
2、從底層SocketChannel中讀取字節,封裝到ByteBuf中。
3、調用Channel的PPLine交給管道中的編解碼器去處理。
4、調用各種事件。
@Override
public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
//每個channel對應一個PPLine
final ChannelPipeline pipeline = pipeline();
//ByteBuf分配器
final ByteBufAllocator allocator = config.getAllocator();
//容量計算器
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
//重置,把之前計數的值全部清空
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
//分配內存,關鍵在於計算分配內存的大小(小了不夠,大了浪費)
byteBuf = allocHandle.allocate(allocator);
//doReadBytes,從socket讀取字節到byteBuf,返回真實讀取數量
//更新容量計算器
allocHandle.lastBytesRead(doReadBytes(byteBuf));
//如果小於0 則socket關閉,如果等於0則沒讀取到數據
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
//釋放資源
byteBuf.release();
byteBuf = null;
//如果小於0則意味着socket關閉
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.fireChannelRead(byteBuf);
byteBuf = null;
//判斷是否繼續從socket讀取數據
} while (allocHandle.continueReading());
//讀取完成後調用readComplete,重新估算內存分配容量
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
//
//根據情況移除OP_READ事件
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
在讀取中遇到異常,或者返回EOF(-1),說明底層通道關閉,需要處理關閉邏輯。
private void closeOnRead(ChannelPipeline pipeline) {
//底層輸入流未關閉
if (!isInputShutdown0()) {
//是否允許辦關閉
if (isAllowHalfClosure(config())) {
//關閉輸入流
shutdownInput();
pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
} else {
//關閉socket
close(voidPromise());
}
} else {
//激發事件
inputClosedSeenErrorOnRead = true;
pipeline.fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE);
}
}
異常處理的邏輯
//異常處理
private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close,
RecvByteBufAllocator.Handle allocHandle) {
if (byteBuf != null) {
//如果byteBuf有數據,則交給管道處理
if (byteBuf.isReadable()) {
readPending = false;
pipeline.fireChannelRead(byteBuf);
} else {
//釋放資源
byteBuf.release();
}
}
//容量計算機更新guess大小
allocHandle.readComplete();
//激發讀完成事件和異常事件
pipeline.fireChannelReadComplete();
pipeline.fireExceptionCaught(cause);
//關閉socket
if (close || cause instanceof IOException) {
closeOnRead(pipeline);
}
}
關閉socket的邏輯,需要釋放輸出隊列緩存,觸發關閉事件,關閉底層socket等。
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.
//調用底層關閉socket
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);
}
//激發fireChannelInactive事件
//激發fireChannelUnregistered事件
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);
}
}
if (inFlush0) {
invokeLater(new Runnable() {
@Override
public void run() {
fireChannelInactiveAndDeregister(wasActive);
}
});
} else {
fireChannelInactiveAndDeregister(wasActive);
}
}
}
技術交流QQ:212320390
關注公衆號