新连接检测
NioEventLoop:
/**
* 以新连接接入分析处理过程为例
* @param k
* @param ch
*/
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 {
/**
* 当检测到服务端的channel有事件发生。
*/
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
/**
* 对应的连接事件发生,unsafe.read 实际上是AbstractNioMessageChannel.read的方法
*/
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
AbstractNioMessageChannel
@Override
/**
* 调用具体的读事件处理逻辑
*/
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
/**
* allocHandle用于控制客户端接入速率,每次最多读16个连接
*/
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
/**
* readBuf是用于承载连接的容器
*/
/**
* 读readMessage 如果返回的是0,代表没有连接可读,如果返回1代表读取了一个连接,
* 还要在进行doReadMessages(readBuf)调用来判断当前是不是还有连接未处理
*/
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (continueReading(allocHandle));
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
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 {
// 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
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
NioServerSocketChannel
doReadMessages:
@Override
/**
* 读取出一个新连接,即java底层的channel
* 然后封装成netty的NioSocketChannel,添加到承载的容器中
* 如果读取到连接,就返回1,否则返回0
*/
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
try {
if (ch != null) {
/**
* 将服务端channel与jdk底层的channel作为构造函数参数放进去。
*/
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;
}
创建NioSocketChannel
创建NioSocketChannel时候主要做了两件事情,第一逐层调用父类方法,初始化id。unsafe,pipeline对象,
设置非阻塞模式,保存读事件。第二:创建NioSocketChannel配置类,禁用tcpnodelay算法。已达到快速发送的效果
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
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);
}
}
// 禁用tcpNoDelay
public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
super(channel);
this.javaSocket = ObjectUtil.checkNotNull(javaSocket, "javaSocket");
// Enable TCP_NODELAY by default if possible.
if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
try {
setTcpNoDelay(true);
} catch (Exception e) {
// Ignore.
}
}
}
channel分类
新连接分配线程和selector注册
回到现在刚拿到一条连接的过程。接着调用pipeline.fireChannelRead(新连接),将获取到的新连接通过
服务端的pipeline进行传递。在服务端channel初始化的时候如下图,设置了服务端的handler。
而这个hander,所做的事情就包括:
添加childhandler
设置options与attrs
选择NioEventloop并注册selector
pipeline.fireChannelRead(新连接)这个新连接就会传播到ServerBootstrapAcceptor的channelread方法中
@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);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
}
}
}
ServerBootstrapAcceptor.java
属性的由来:childGroup,childHandler,childOptions,childAttrs都是在服务端channel初始化的时候
在ServerBootstrap启动类中执行init方法,传递进去的,这些属性我是我们在启动类设置的。所以在下面
channelread方法中可以拿到。
ServerBootstrapAcceptor(
final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;
}
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
// 添加我们自定义的chilhandler
// 添加childhandler就是ChannelInitializer,触发ChannelInitializer的handlerAdded方法
// 然后在handlerAdd方法中调用我们重写的init方法,进行handler添加,然后自己删除ChannelInitializer
child.pipeline().addLast(childHandler);
// 设置自定义tcp属性
setChannelOptions(child, childOptions, logger);
// 设置我们自定义的key
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
在这里就会选择一个nioeventloop并注册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);
}
}
register:
@Override
public ChannelFuture register(Channel channel) {
// next是返回一个nioeventloop,在eventloop中执行方法。
return next().register(channel);
}
然后调用SingleThreadEventLoop的register方法
@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(Nioeventloop), promise);
return promise;
}
最终调用到AbstractChannel的register方法
接下来注册逻辑就和服务端一样了
从AbstractChannel的register()
```java
@Override
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;
}
保存当前的eventloop
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
}
}
}
最终调用AbstractNioChannel.java的doRegister方法
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
向selector注册读事件
register之后调用firechannelactive,进行事件传播,事件传播从pipeline的head头结点开始向下传播。在头结点的channelActive中会调用 readIfIsAutoRead();最后会传播到AbstractChannel的AbstractUnsafe的beginread方法,这个beginread方法最终会调用到AbstractNioChannel的dobeginread方法。然后向selector注册读事件。
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
回到之前注册jdk底层channel的时候,返回了一个selectionkey,对于客户端channl注册的感兴趣事件为0,
同时在创建客户端channel的时候,见下面代码,保存的readInterestOp为read事件
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
如果是客户端chnanel返回的是0
final int interestOps = selectionKey.interestOps();
所以下面的代码表示已经绑定成功了,告诉selector要关心一个read事件,selector发现如果有数据可读
接入就回交由netty来处理
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
```java
public NioSocketChannel(ServerSocketChannel channel) {
/**
* SelectionKey.OP_ACCEPT:相当于注册感兴趣事件为读事件
*/
super(parent, ch, SelectionKey.OP_READ);; ----》super(parent, ch, readInterestOp);
/**
* 创建tcp参数配置类,比如backlog reuseaddr ,专属于单独的服务端或者客户端channel
*/
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}