在NioEventLoop#processSelectedKey方法里这么一段代码
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
这里就有处理新连接接入的事件,现在跟进去到AbstractNioMessageChannel内部类NioMessageUnsafe的read方法
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
//allocHandle主要用来控制读取连接的速率
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) {
//省略
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
//处理每个新接入的连接,为每个新接入的连接分配NioEventLoop和注册selector
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
//关闭和处理异常,省略
} finally {
//省略
}
}
概括一下这里主要做的几件事
- 检测新连接,控制新连接接入速率
- 为每个新连接创建
NioSocketChannel - 为每个
NioSocketChannel分配EventLoop,注册channel,向selector上注册读事件
检测新接入的连接
来看看检测新接入的连接这段代码
do {
//读取新接入的连接并返回读取的数目
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
//若读取不到新连接,跳出循环
break;
}
if (localRead < 0) {
closed = true;
break;
}
//增加已连接的客户端数
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
这里来看看循环条件里的continueReading方法,这个方法在DefaultMaxMessagesRecvByteBufAllocator类的内部类MaxMessageHandle里
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
return config.isAutoRead() &&
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
totalMessages < maxMessagePerRead &&
totalBytesRead > 0;
}
简单概括一下,就是控制每次总共读取的连接数不能超过一定数目
创建NioSocketChannel
跟进上面的doReadMessages方法,来到NioServerSocketChannel#doReadMessages这个方法
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
//注意这里将新创建的NioSocketChannel放入list中,后面处理新连接接入需要用到
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
//省略
}
return 0;
}
可以看到,这里创建了一个NioSocketChannel
继续跟进看NioSocketChannel的构造函数
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
super方法调用父类构造函数,调用configureBlocking(false)设置非阻塞,设置id,unsafe,pipeline等成员变量,并且设置关注的事件为read事件(注意只是设置成员变量,并未注册到selector)
另外值得注意的是,super方法调用父类AbstractNioByteChannel的构造函数里
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
注意这里传入了一个SelectionKey.OP_READ,这个值会在后面向selector注册读事件时用到
NioSocketChannel的构造函数里还创建了一个NioSocketChannelConfig,值得注意的是这个类的构造函数调用父类禁止了Nagle算法(Nagle算法将小的数据包封装成大的数据包再发送出去,而netty禁止这个算法,让数据包能够及时发送出去)
分配NioEventLoop和注册selector
接下来看处理每次新接入的连接的代码
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
//pipeline为服务端的pipeline
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf是一个list,里面装的就是之前创建的NioSocketChannel
之前解析过服务端启动的代码,在那里提到,在往pipeline里添加handler时,会额外添加一个特殊的handler,即ServerBootstrapAcceptor,这个handler就是用来处理服务端的接入的
一般情况下,服务端pipeline里的handler排列情况一般是 Head——ServerBootstrapAcceptor——Tail 这种样
现在这里pipeline发送了一个read事件,那么就会从head节点开始传播到ServerBootstrapAcceptor
接下来看看ServerBootstrapAcceptor的channelRead方法,注意这个类是ServerBootstrap的内部类
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
//添加childHandler
child.pipeline().addLast(childHandler);
//设置用户传入的childOptions(用于channel)
setChannelOptions(child, childOptions, logger);
//设置用户传入的childAttrs(tcp参数等)
setAttributes(child, childAttrs);
try {
//注册传入的NioSocketChannel,主要是选择eventloop和注册selector
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);
}
}
添加的childHandler通常是是用户在调用childHandler时传入的一个特殊的handler即ChannelInitializer,这个handler在被添加后会调用我们重写的initChannel方法添加我们自己的handler
接下来register方法,这里会调用到MultithreadEventLoopGroup#register方法
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
这里的next方法会调用线程选择器选择一个NioEventLoop(线程选择器在netty——NioEventLoopGroup创建里说过)
接下来这里的register方法会调用到SingleThreadEventLoop#register方法,最终调用到AbstractUnsafe类的register方法(这个类在AbstractChannel类里)
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "eventLoop");
//省略无关代码
AbstractChannel.this.eventLoop = eventLoop;
//由于当前是在服务端的eventloop里而非选择器保存的eventloop里,所以此处会走向else分支
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
//省略
}
}
}
接下来看看register0方法
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
//调用jdk底层的SelectableChannel类的register方法注册channel
doRegister();
neverRegistered = false;
registered = true;
//发送handler添加事件
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
//发送channel注册成功事件
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
//这里是第一次注册,会发送channelActive事件
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
//省略
}
}
这里主要做了
- 调用jdk底层的SelectableChannel类的register方法注册channel
- 发送handler添加事件
- 发送channel注册成功事件
- 发送channelActive事件
注意到,这里发送ChannelActive事件会从头结点开始传播Active事件,这其中会调用到DefaultChannelPipeline#channelActive这个方法
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
在readIfIsAutoRead这个方法里,最终会调用到AbstractNioChannel#doBeginRead这个方法
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();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
在这里,就会向selector注册读事件