當服務端啓動好了之後,也就是說,服務端已經在執行
NioEventLoop
的一個死循環方法run()
中,一直輪詢事件,並且此時的監聽的事件爲OP_ACCEPT
。如果有新連接接入,那麼首先會在上述的run()
方法中觸發…
收到新的連接
首先,服務端啓動好了之後,會進入等待事件的狀態,也就是調用JDK的NIO的API:
// NioEventLoop.java -> run()
if (!hasTasks()) {
strategy = select(curDeadlineNanos);
}
// 核心是調用jdk的api
selector.select();
收到新的連接後,將會通過processSelectedKeys()
進行處理,處理內容包括:創建、初始化NioSocketChannel
。
// NioEventLoop.java
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// 幫助GC
selectedKeys.keys[i] = null;
// attachment是NioServerSocketChannel
// 服務端註冊selectionKey時傳入
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);
}
//...
}
}
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
// ...
try {
// 此時的readyOps爲OP_ACCEPT,也就是16,即二進制10000
int readyOps = k.readyOps();
// ...
// 處理新連接的邏輯開始
// SelectionKey.OP_READ | SelectionKey.OP_ACCEPT = 10001
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 此處的unsafe與創建連接時的unsafe不是同一個實現
unsafe.read();
}
}
// ...
}
創建、初始化NioSocketChannel
上面的unsafe的具體實現是在一個叫做NioMessageUnsafe
的內部類中,在它的read方法中:
①創建了NioSocketChannel
。②通過pipeline中的Handler,即ServerBootstrap$ServerBootstrapAcceptor中初始化NioSocketChannel。
//
@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 {
// ①創建了NioSocketChannel,並加入到readBuf這個List中
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;
// ②在ServerBootstrap$ServerBootstrapAcceptor中初始化NioSocketChannel
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
// ...
}
// ...
}
創建
創建NioSocketChannel的主要流程,就是先通過調用JDK的API獲取SocketChannel,然後再將其作爲一個值傳給NioSocketChannel。因此從另一個方面來看,可以理解成NioSocketChannel是SocketChannel的封裝。
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
// 實際調用時serverSocketChannel.accept()
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
// 創建NioSocketChannel
buf.add(new NioSocketChannel(this, ch));
return 1;
}
}
// ...
return 0;
}
初始化
主要依靠pipeline中的相應事件傳遞。比如說,將channel註冊到EventLoop中這個事件,就是靠pipeline中的Handler,ServerBootstrapAcceptor來完成。
// 調用pipeline中的不可覆蓋方法fireChannelRead
pipeline.fireChannelRead(readBuf.get(i));
// 與之前的流程類似,從pipeline中的頭handler開始傳遞事件
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
// 一個靜態模板方法,掉用next的invokeChannelRead()方法
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
// 還是一個模板方法,用於真實執行hander的read事件處理方法
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
// 觸發handler的read事件,模板模式
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
到這裏,事件的往後續handler傳遞,都是調用上面的這個兩個方法,來執行後續handler的相應read方法。此時pipeline中的handler有:
1) DefaultChannelPipeline$HeadContext
2) io.netty.handler.logging.LoggingHandler
3) io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor
4) DefaultChannelPipeline$TailContext
其中,head對read只是做簡單的傳遞:
// DefaultChannelPipeline$HeadContext
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.fireChannelRead(msg);
}
// AbstractChannelHandlerContext.java
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
// 調用上面代碼片段中的靜態模板方法,實現事件傳遞
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
對LoggingHandler
而言,簡易打印日誌,並往後傳遞事件:
// LoggingHandler.java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "READ", msg));
}
ctx.fireChannelRead(msg);
}
到ServerBootstrapAcceptor
的read方法時,初始化便真正地開始了(此時的線程爲bossGroup中的EventLoop):
// ServerBootstrapAcceptor
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
// 將ChannelInitializer添加到pipeline中,等執行完initial方法後,會被移除
child.pipeline().addLast(childHandler);
// 設置NioSocketChannel屬性
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
try {
// 將NioSocketChannel綁定到一個workGroup中的NioEventLoop上
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
register的過程與服務端啓動時的綁定類似,先選出一個EventLoop,選的時候,有兩種方式,根據不同的線程數,使用不同的選擇方式。然後經過輾轉,來到對register0()
的執行,這個方法時主要的register操作。但是此時的線程是bossGroup中的EventLoop,而register0()
會在workGroup中的線程中執行。所以會先將task放入隊列中,然後啓動線程,並進入NioEventLoop的run()死循環方法,通過不斷遍歷是否有已監聽事件以及執行隊列中的任務,最終來執行該task。
// MultithreadEventLoopGroup.java
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
// SingleThreadEventLoop.java
@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;
}
// AbstractChannel.java
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
// ...
AbstractChannel.this.eventLoop = eventLoop;
// 此時的線程是bossGroup中的EventLoop,
// 此處的eventLoop則爲上面分配的wordGroup中的線程。
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
// 所以會執行此方法,此時該eventLoop中的線程還未啓動,會將此task放入隊列中
// 然後會通過eventLoop.execute來啓動線程,並進入NioEventLoop的run()方法
// 通過不斷遍歷是否有已監聽事件以及執行隊列中的任務,最終來執行該task。
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
// ...
}
}
此時,線程切換到workGroup中的EventLoop。主要執行好幾個操作:先調用jdk的api,註冊selectionKey;再發布相應的事件;最後修改interestOps爲OP_READ。
private void register0(ChannelPromise promise) {
try {
// ...
boolean firstRegistration = neverRegistered;
// 調用jdk的api,註冊selectionKey
doRegister();
neverRegistered = false;
registered = true;
// 處理ChannelInitializer,並移除掉它
pipeline.invokeHandlerAddedIfNeeded();
// 在服務端啓動的時候,會以觀察者模式調用操作完成的Listener
// doBind操作就是這樣被封裝到了其中,但處理客戶端連接沒有doBind操作
safeSetSuccess(promise);
// 從pipeline的head開始傳遞registered事件
pipeline.fireChannelRegistered();
// 此時已經被激活
if (isActive()) {
// 第一次進行register操作,被視爲建立連接
if (firstRegistration) {
// 與服務端類似,會在此處將監聽的事件改爲OP_READ
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// 真的有數據來了
beginRead();
}
}
}
// ...
}
調用jdk的api,註冊selectionKey
// AbstractNioChannel.java
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 監聽的事件爲0,attachment是channel自己
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
}
// ...
}
}
對channle已激活的事件傳遞中,會將NioSocketChannel的interestOps修改爲OP_READ。下面的代碼是事件在pipeline中的傳遞,與上面的分析內容一致,在此不多贅述。
// DefaultChannelPipeline.java
@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
// AbstractChannelHandlerContext.java
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}
private void invokeChannelActive() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
唯一有區別的是:pipeline中的head,即HeadContext對active事件的處理方式,多了一塊對interestOps的處理:
// DefaultChannelPipeline.java $ HeadContext
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
// 修改interestOps
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
這裏會在pipeline中傳遞read事件,但是是從tail開始,可以直接跳到TailContext的read()方法中:
// AbstractChannel.java
@Override
public Channel read() {
pipeline.read();
return this;
}
// DefaultChannelPipeline.java
@Override
public final ChannelPipeline read() {
tail.read();
return this;
}
// AbstractChannelHandlerContext.java
@Override
public ChannelHandlerContext read() {
final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeReadTask);
}
return this;
}
private void invokeRead() {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
// DefaultChannelPipeline.java
@Override
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
此處便到了修改的interestOps的主要邏輯處:
// AbstractChannel.java
@Override
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
// AbstractNioChannel.java
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
// 此時interestOps爲0,所以if中的條件一定會成立
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
至此,對新建連接的處理基本完成。