客户端接入
当客户端调用connect后,服务端reactor线程在循环过程中会检查到这个事件,然后在processSelectedKey方法中处理。我们在上面也已经提到了这个方法,我们继续跟进,当客户端接入后,服务端发生了什么。
//读事件和连接事件就绪,执行unsafe.read()
//如果是boss线程则是OP_ACCEPT事件,如果是work线程则是OP_READ事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
上面代码我们可以看到,当客户端接入后是OP_ACCEPT事件,这里会走到unsaferead()方法,我们继续跟进为是服务端所以来到了AbstractNioMessageChannel的read方法
public void read() {
//内部线程执行
assert eventLoop().inEventLoop();
//channel配置
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
//AdaptiveRecvByteBufAllocator内部HandleImpl
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
//重置初始化值
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
//如果有客户端接入,localRead返回值为1
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;
//触发ChannelRead事件,最后传到了ServerBootstrapAcceptor的channelRead方法
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();
}
}
}
}
我们将上面代码分开看
//内部线程执行
assert eventLoop().inEventLoop();
//channel配置
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
//AdaptiveRecvByteBufAllocator内部HandleImpl
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
//重置初始化值
allocHandle.reset(config);
首先保证必须在内部线程执行,然后赋值ChannelConfg和ChannelPipeline,allocHandle是什么呢?我们走到创建方法中AbstractUnsafe的recvBufAllocHandle方法,recvHandle第一次调用时初始值就是null
public RecvByteBufAllocator.Handle recvBufAllocHandle() {
if (recvHandle == null) {
recvHandle = config().getRecvByteBufAllocator().newHandle();
}
return recvHandle;
}
通过config()getRecvByteBufAllocator()获取的就是我们在前面分析过得在创建NioServerSocketChannel时,在创建NioServerSocketChannelConfig时创建的AdaptiveRecvByteBufAllocator
public NioServerSocketChannel(ServerSocketChannel channel) {
//1.调用父类构造方法
super(null, channel, SelectionKey.OP_ACCEPT);
//2.创建配置类
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
public DefaultChannelConfig(Channel channel) {
//byteBuf分配器
this(channel, new AdaptiveRecvByteBufAllocator());
}
所以上面的newHandle的方法会走到AdaptiveRecvByteBufAllocator中可以看到创建的就是HandleImpl
public Handle newHandle() {
return new HandleImpl(minIndex, maxIndex, initial);
}
创建完成后调用reset方法设置初始值,由于Handlelmpl是MaxMessageHandle的子类所以这里走到 DefaultMaxMessagesRecvByteBufAllocator的内部类MaxMessageHandle的reset方法
public void reset(ChannelConfig config) {
this.config = config;
maxMessagePerRead = maxMessagesPerRead();
totalMessages = totalBytesRead = 0;
}
我们可以看到这里是设置 些初始值,maxMessagePerRead是每次读取连接的最大值 默认为16个
totalMessages为总连接数,totalBytesRead为总共读取的字节数,全部设置为0。我们继续分析循环结构
try {
do {
//如果有客户端接入,localRead返回值为1
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;
}
我们先看do…while循环中的操作,doReadMessages处理客户端接入
protected int doReadMessages(List<Object> buf) throws Exception {
//执行accept方法返回连接的channel
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
//如果channel不为null
if (ch != null) {
//将接入的nio底层的channel,放入新创建的NioSocketChannel实例中
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
可以看到首先调用nio底层的accept方法,其实就是ServerSocketChannel执行accept方法返回接入的
SocketChannel,然后将返回的SocketChannel包装在一个NioSocketChannel中,而NioSocketChannel的创建我们在上面也已经提到过,我们再来看一次
public NioServerSocketChannel(ServerSocketChannel channel) {
//1.调用父类构造方法
super(null, channel, SelectionKey.OP_ACCEPT);
//2.创建配置类
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;//nio的ServerSocketChannel
this.readInterestOp = readInterestOp;//连接事件
try {
ch.configureBlocking(false);//设置非阻塞模式
} catch (IOException e) {
try {
ch.close();//发生异常就关闭channel
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
设置一些属性值,设置连接事件和nio底层的代码,将SocketChannel设置为非阻塞,继续跟踪
protected AbstractChannel(Channel parent) {
this.parent = parent;//null
//1.channel唯一标识id
id = newId();
//2.unsafe是channel的内部类,封装了很多底层nio的操作
unsafe = newUnsafe();
//3.每创建一个channel就会创建一个channelPipe用于组织handler
pipeline = newChannelPipeline();
}
这里面传入了parent值就是NioServerSocketChannel,然后进行赋值,创建channel唯一id,然后创建unsafe对象
protected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe();
}
最后给channel创建一个pipeline
public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket;
}
public DefaultChannelConfig(Channel channel) {
//byteBuf分配器
this(channel, new AdaptiveRecvByteBufAllocator());
}
我们继续回到doReadMessages中,将创建好的
NioSocketChannel添加到了buf集合中,然后返回1,然后调用allocHandleincMessagesRead(localRead)统计接入的连接数。我们看下跳出do…while循环的条件是什么
while (allocHandle.continueReading());
public boolean continueReading() {
return continueReading(defaultMaybeMoreSupplier);
}
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
//config.isAutoRead()默认为true
return config.isAutoRead() &&
//respectMaybeMoreData默认为true
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
//已经读取到的总连接数是不是超过了maxMessagePerRead,默认是16
totalMessages < maxMessagePerRead &&
//totalBytesRead设置为0,一直为false
totalBytesRead > 0;
}
这里我们可以看到是一些条件的组合我们分开看
- configisAutoRead()默认值就是true
- (!respectMaybeMoreDatamaybeMoreDataSupplierget()):respectMaybeMoreData的默认值就是 true,maybeMoreDataSupplierget()我们跟进到方法中其实就是比较attemptedBytesRead == lastBytesRead这里初始值都是0所以返回true
- totalMessages<maxMessagePerRead,判断接入的连接数是不是超过了最大值,最大值默认为16 totalBytesRead>0这里我们前边已经分析过
- totalBytesRead的初始化赋值为0,所以这里返回false
也就是默认只要有一个连接接入就会跳出这个循环
我们继续分析
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
//触发ChannelRead事件,最后传到了ServerBootstrapAcceptor的channelRead方法
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
这里会遍历刚才加入到集合中的NioSocketChannel,然后触发channelRead事件,而我们在分析服务端启动流程的时候说过,pipeline中会加入一个ServerBootstrapAcceptor,这里就会触发该类的channelRead方法
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//就是传过来的NioSocketChannel内部包装了接入的channel
final Channel child = (Channel) msg;
//向传过来的NioSocketChannel的pipeline中添加childHandler
child.pipeline().addLast(childHandler);
//设置配置
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
//将传过来的NioSocketChannel注册到childGroup上
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);
}
}
将传过来的对象强转为Channel对象,因为我们传过来的就是NioSocketChannel,然后向channel中添加了 childHandler,这里我们知道就是我们在服务端启动过程中分析过得添加的就是Channellnitializer,然后设置些配置,来到了重点方法register,这个childGroup就是我们前面创建的workGroup,然后执行register方法这个方法我们在上面也提到了,我们跟进方法中
public ChannelFuture register(Channel channel) {
//1.创建一个DefaultChannelPromise用于返回执行结果
return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");//校验
//调用unsafe的register方法
promise.channel().unsafe().register(this, promise);
return promise;
}
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("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;
}
//关联channel和eventLoop
AbstractChannel.this.eventLoop = eventLoop;
//1.当前运行的线程是eventLoopGroup线程,及内部线程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
//2.调用execute方法将register0方法包装成一个任务放入NioEventLoop的
//任务队列中,由NioEventLoop线程执行
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);
}
}
}
这里调用的是boss线程所以会走到else中,将注册方法封装成一个普通任务添加到workGroup的eventLoop中。这里我们在前面也已经分析过,就会将workGroup的NioEventLoop线程启动,我们继续跟踪到register0方法中
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
//1.设置第一此执行注册标志
boolean firstRegistration = neverRegistered;
//2.完成channel注册,返回连接selectionKey
doRegister();
neverRegistered = false;
registered = true;
//3.检查是否触发HandlerAdded事件
pipeline.invokeHandlerAddedIfNeeded();
//4.设置promise注册成功
safeSetSuccess(promise);
//5.触发ChannelRegistered事件
pipeline.fireChannelRegistered();
//6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
//7.给channel注册读事件
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
调用底层的注册方法
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//1.执行nio底层的register方法,并且attch了channel,不关系任何事件
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
//2.强行执行一次selectNow,取消了的SelectionKey可能还在缓存中未消除
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
可以看到执行了nio底层的注册方法,并且不关注任何事件,并attch上AbstractNioChannel。然后执行 pipeline.invokeHandlerAddedIfNeeded()方法
//3.检查是否触发HandlerAdded事件
pipeline.invokeHandlerAddedIfNeeded();
这个方法我们在上面也分析过就是执行Channelnitializer的init方法,将channelHandler加入到流水线中。 然后执行
//4.设置promise注册成功
safeSetSuccess(promise);
设置promise值触发回调listener的方法
//5.触发ChannelRegistered事件
pipeline.fireChannelRegistered();
触发ChannelRegistered事件
//6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
//7.给channel注册读事件
beginRead();
}
}
这里由于channel已经完成连接,所有isActive()返回true,由于是第一次注册所有会触发ChannelActive事件,然后执行beginRead()方法
public final void beginRead() {
//1.校验
assertEventLoop();
//当前是不是活跃状态
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
//1.判断selectionKey是不是有效的
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
//2.获取关心的事件,这里其实什么都没关注
final int interestOps = selectionKey.interestOps();
//3.如果什么事件都不关系,则设置关注OP_ACCEPT事件
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
最后设置NioSocketChannel关注的事件为读事件,如果发生错误,就触发一些channel的关闭操作。我们继续回到io.nettychannel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read中
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
allocHandlereadComplete()对于接入操作没做什么重要的操作,然后触发ChannelReadComplete事件,如果上面过程中发生了错误就触发ExceptionCaught事件,如果设置了关闭就执行一些关闭操作
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
如果不是AutoRead设置不是1并且readPending设置为false就将channel关注的读事件移除。 到这里客户端的接
已经分析结束,客户端连接对应的channel已经注册到了workGroup中NioEventLoop中的selector上,之后客户端的read和write事件就通过work线程来处理,这里也可以看到boss线程组主要负责客户端的连接,而work线程组主要负责客户端连接后的读写事件。如果有分析错误的地方还请不吝指正。感谢!!!