绪论
两个问题
- netty是在哪里检测到有新连接接入的?
- 新连接是怎样注册到NioEventLoop线程的?
处理逻辑
- 检测新连接
- 创建NioSocketChannel
- 分配线程及注册selector
- 向selector注册读事件
检测新连接
processSelectedKey(key, channel) [入口]
- NioMessageUnsafe.read()
- doReadMessages()
- javaChannel().accept()
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
......
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());
}
}
进入AbstractNioMessageChannel.NioMessageUnsafe.read()方法
public void read() {
assert eventLoop().inEventLoop(); //确保线程在NioEventLoop方法内调用
final ChannelConfig config = config(); //服务端config
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle(); //处理服务端接入速率
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
int localRead = doReadMessages(readBuf); //readBuf--存取临时读到的连接
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
......
}
}
}
进入doReadMessages()方法
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel()); //获取服务端启动过程中创建的JDK底层channel
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch)); //把词channel封装成NioSocketChannel放进buf中
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;
}
进入continueReading()方法
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
return config.isAutoRead() &&
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
totalMessages < maxMessagePerRead &&
totalBytesRead > 0;
}
totalMessages -- 读到的连接
maxMessagePerRead -- 每次可以读到的最大连接,默认为16
创建NioSocketChannel
进入方式
processSelectedKey(key, channel)-->NioMessageUnsafe.read()-->doReadMessages()-->new NioSocketChannel(this, ch)
其中,this指NioServerSocketChannel,ch指客户端channel。进入NioSocketChannel()方法:
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
这里可以分为两个分支。
首先
进入super()方法:
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ); //若有事件读写,会发出通知
}
继续进入super()方法:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch; //保存客户端channel
this.readInterestOp = readInterestOp; //传进来的读事件
try {
ch.configureBlocking(false); //设为非阻塞模式
} catch (IOException e) {
try {
ch.close();
} 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);
}
}
继续进入super()方法:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
其次,第二个分支
进入NioSocketChannelConfig()方法:
public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket; //保存socket
// Enable TCP_NODELAY by default if possible.
if (PlatformDependent.canEnableTcpNoDelayByDefault()) { //看是否支持安卓,不是返回true
try {
setTcpNoDelay(true);
} catch (Exception e) {
// Ignore.
}
}
}
进入setTcpNoDelay(true)方法:
public SocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
try {
javaSocket.setTcpNoDelay(tcpNoDelay);
} catch (SocketException e) {
throw new ChannelException(e);
}
return this;
}
public void setTcpNoDelay(boolean on) throws SocketException { //进制Nagle算法,小的数据包发出去会降低延迟
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on));
}
新连接NioEventLoop分配及注册selector
在服务端启动过程中,会专门添加一个channelHandler,专门处理新连接接入逻辑。在服务端启动的过程中,init()方法中会把服务端channel传入进来,然后初始化该channel。在接下来的代码中,会默认添加ServerBootstrapAcceptor()连接处理器,将其他用户自定义属性传进去。服务端启动init()方法
ServerBootstrapAcceptor
服务端Channel的pipeline构成
Head --> ServerBootstrapAcceptor --> Tail
客户端的每一条连接都会经历上述过程传播。
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@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 {
int localRead = doReadMessages(readBuf); //已经创建了客户端channel
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;
//对客户端每一条连接,调用服务端的pipeline中的fireChannelRead方法将客户端的
//连接传入到ServerBootstrapAcceptor()
pipeline.fireChannelRead(readBuf.get(i));
}
ServerBootstrapAcceptor()主要做以下事情:
- 添加childerHandler;
- 设置options和attrs;
- 选择NioEventLoop并注册selector;
添加childerHandler
此childerHandler实际是ChannelInitializer,里面会有一个initChannel()方法,可以拿到pipeline,让用户自定义handler。
进入ChannelInitializer类中的方法:
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// This should always be true with our current DefaultChannelPipeline implementation.
// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
// will be added in the expected order.
if (initChannel(ctx)) {
// We are done with init the Channel, removing the initializer now.
removeState(ctx);
}
}
}
@SuppressWarnings("unchecked")
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) { // Guard against re-entrance.
try {
initChannel((C) ctx.channel()); //首先回调到起始位置,添加handler
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this); //最终将自身remove
}
}
return true;
}
return false;
}
设置options和attrs
向selector注册读事件