前一章节我们分析了客户端启动(Bootstrap)的流程,接下来我们就分析下服务端的一个启动流程吧。代码来自Netty官方example的echo示例。
/**
* Echoes back any received data from a client.
*/
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
和客户端代码相比大致是相同的,只有些许配置部分不同。
- 配置EventLoopGroup(NioEventLoopGroup);
- 配置Channel(NioServerSocketChannel);
- 配置Handler
配置EventLoopGroup
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
在服务端这里我们配置了2个EventLoopGroup,之所有要配置2个是因为,Netty将其分别处理不同的任务,1个用来处理客户端的连接,一个处理客户端的IO任务,各司其职,才能更加高效的完成网络任务,如下图任务所示。
这里的BossGroup配置调用了父类AbstractBootstrap的构造方法,如下所示:
public B group(EventLoopGroup group) {
ObjectUtil.checkNotNull(group, "group");
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return self();
}
也就是我们配置的BossGroup最终是放到了ServerBootstrap的group字段中,而WorkerGroup放到了ServerBootstrap的childGroup中。
配置Channel
继续往下走便到了配置Channel的类型了,因为是服务端所以我们这里配置的是一个NioServerSocketChannel.class:
public B channel(Class extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
这里我们实例化了一个ReflectiveChannelFactory类,通过名字可以知道这是一个Channel反射工厂类,看下其实现:
public class ReflectiveChannelFactory implements ChannelFactory {
private final Constructor extends T> constructor;
public ReflectiveChannelFactory(Class extends T> clazz) {
this.constructor = clazz.getConstructor();
}
@Override
public T newChannel() {
return constructor.newInstance();
}
}
通过源码可以看出,该类的作用是根据传递进来的Channel类型获取对应的默认构造方法,最后通过newChannel方法实例化Channel对象。
实例化ReflectiveChannelFactory对象后,通过channelFactory方法设置对应的ChannelFactory对象
public B channelFactory(io.netty.channel.ChannelFactory extends C> channelFactory) {
return channelFactory((ChannelFactory) channelFactory);
}
配置handler
public B handler(ChannelHandler handler) {
this.handler = ObjectUtil.checkNotNull(handler, "handler");
return self();
}
这里我们配置了一个LoggingHandler并且设置日志级别为LogLevel.INFO。
配置ChildHandler
public ServerBootstrap childHandler(ChannelHandler childHandler) {
this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
return this;
}
这里使用了Netty提供的一个特殊ChannelHandler抽象类ChannelInitializer,这里我们先不表,后面的内容会讲到该内容。
绑定
当所有的参数设置好之后,就到了我们绑定的阶段了,先来看下代码实现吧:
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
public ChannelFuture bind(SocketAddress localAddress) {
//校验group和ChannelFactory是否为空,如果为空则会抛出IllegalStateException异常
validate();
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化并注册Channel
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
//绑定本地端口
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
这里我们先看下initAndRegister方法,这个方法我们在Bootstrap中也有讲到过,这里继续来看下:
final ChannelFuture initAndRegister() {
//...省略部分代码
Channel channel = channelFactory.newChannel();
init(channel);
return regFuture;
}
该方法中使用到了ChannelFactory的newChannel方法,在之前我们提到过ChannelFactory是用于将给定的Channel类型类,然后通过反射构造方法进行实例化对象,所以我们这里实例化的对象为NioServerSocketChannel。接下来看下该类的一个结构图。
再来看下NioServerSocketChannel的默认构造方法里做了哪些处理吧。
public class NioServerSocketChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.ServerSocketChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static ServerSocketChannel newSocket(SelectorProvider provider) {
return provider.openServerSocketChannel();
}
private final ServerSocketChannelConfig config;
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(SelectorProvider provider) {
this(newSocket(provider));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
}
在NioServerSocketChannel默认构造函数中,调用了newSocket方法,通过该方法开启了一个服务端的ServerSocketChannel,最终调用父类AbstractNioChannel的构造方法:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
ch.configureBlocking(false);
}
在NioServerSocketChannel中我们配置感兴趣的事件为SelectionKey.OP_ACCEPT
,代表只监听连接事件,父类AbstractNioChannel这里设置了Channel事件,可以看到这里还调用了父类方法将parent参数传递进去,来看下具体实现。
protected AbstractChannel(Channel parent) {
this.parent = parent;
//实例化一个DefaultChannelId
id = newId();
//实例化对象为NioMessageUnsafe
unsafe = newUnsafe();
//实例化DefaultChannelPipeline(AbstractHandlerContext为head、tail的双向链表Handler节点)
pipeline = newChannelPipeline();
}
再回到我们的initAndRegister方法,继续往下执行就到了init方法了,因为在父类AbstractBootstrap中该方法是一个抽象方法,所以这个方法的实现是交给ServerBootstrap来实现的,如下所示:
void init(Channel channel) {
//设置Channel的一些网络配置选项
setChannelOptions(channel, newOptionsArray(), logger);
//设置Channel的一些配置属性
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry, Object>[] currentChildOptions;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
final Entry, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
p.addLast(new ChannelInitializer() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
init方法向ChannelPipeline添加了一个ChannelInitializer抽象类Handler,该Handler的initChannel方法会在该Handler添加后调用,看下其实现。
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
//检查是否重复添加
checkMultiplicity(handler);
//实例化一个DefaultChannelHandlerContext
newCtx = newContext(group, filterName(name, handler), handler);
//将其添加到双向链表中
addLast0(newCtx);
//如果registered是false意味着Channel还没有注册到EventLoop.
//在这种情况下,我们将上下文添加到管道中,并添加一个任务,该任务将在注册通道后调用ChannelHandler.handlerAdded(...)
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
这里我们通过断点调试发现registered这个变量是false,这就意味着这时候Channel还没有注册到EventLoop上的,所以我们来看下callHandlerCallbackLater方法实现:
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
assert !registered;
PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
PendingHandlerCallback pending = pendingHandlerCallbackHead;
if (pending == null) {
pendingHandlerCallbackHead = task;
} else {
// Find the tail of the linked-list.
while (pending.next != null) {
pending = pending.next;
}
pending.next = task;
}
}
可以看到这里使用了一个待处理的PendingHandlerAddedTask类对象来处理,看下其实现:
private final class PendingHandlerAddedTask extends PendingHandlerCallback {
PendingHandlerAddedTask(AbstractChannelHandlerContext ctx) {
super(ctx);
}
@Override
public void run() {
callHandlerAdded0(ctx);
}
@Override
void execute() {
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
callHandlerAdded0(ctx);
} else {
//...省略try-catch代码
executor.execute(this);
}
}
}
这个任务采用异步待处理任务来执行任务,最终的任务是通过调用callHandlerAdded0方法来实现的,看下其实现:
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
//...
ctx.callHandlerAdded();
}
这里继续深入AbstractChannelHandlerContext#callHandlerAdded方法:
final void callHandlerAdded() throws Exception {
// We must call setAddComplete before calling handlerAdded. Otherwise if the handlerAdded method generates
// any pipeline events ctx.handler() will miss them because the state will not allow it.
if (setAddComplete()) {
handler().handlerAdded(this);
}
}
到这里我们就清晰了,这里调用的并是ChannelInitializer#handlerAdded方法了:
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
if (initChannel(ctx)) {
//移除该ChannelHandlerContext也就是对应的ChannelInitializer抽象类
removeState(ctx);
}
}
}
这里判断了NioServerSocketChannel是否已经注册了,然后调用initChannel方法,如下:
p.addLast(new ChannelInitializer() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
在initChannel方法中,首先获取ChannelPipeline,这里handler()获取的便是我们之前的配置Handler,如果有配置Handler便将其添加到ChannelPipeline中,这里的handler()我们配置的是LoggingHandler。最后通过绑定在该Channel上的EventLoop线程执行一个异步任务,将ServerBootstrapAcceptor添加到ChannelPipeline中,来看下其实现:
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
//配置的WorkerGroup
private final EventLoopGroup childGroup;
//配置的childHandler
private final ChannelHandler childHandler;
//配置的childOptions
private final Entry, Object>[] childOptions;
//配置的childAttrs
private final Entry, Object>[] childAttrs;
private final Runnable enableAutoReadTask;
ServerBootstrapAcceptor(final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler, Entry, Object>[] childOptions, Entry, Object>[] childAttrs) {
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;
enableAutoReadTask = new Runnable() {
@Override
public void run() {
channel.config().setAutoRead(true);
}
};
}
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//这里的msg便是接收的客户端连接对象Channel(NioSocketChannel)
final Channel child = (Channel) msg;
//将配置的childHandler添加到NioSocketChannel的ChannelPipeline中
child.pipeline().addLast(childHandler);
//设置ChannelOption
setChannelOptions(child, childOptions, logger);
//设置Channel属性
setAttributes(child, childAttrs);
//执行EventLoop与Channel的绑定工作
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
}
}
有好奇的小伙伴可能会有疑问channelRead方法是在什么时候调用的,我也比较好奇,不过这里先不表。
再回到我们的initAndRegister方法,继续往下执行:
//...
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
//...
这里主要关注register(channel)方法,这里group()方法也就是我们之前配置的NioEventLoopGroup(BossGroup),跳该方法是由父类MultithreadEventLoopGroup实现的,代码如下:
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
通过next()方法我们返回的是NioEventLoop对象,该方法也是由父类SingleThreadEventLoop来实现的,如下:
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
这里又有熟悉的Unsafe对象,其实现为NioMessageUnsafe,该方法也是由父类AbstractUnsafe来实现的,如下:
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "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;
}
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
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);
}
}
}
这里我们跟进register0方法即可:
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
doRegister();
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
// Only fire a channelActive if the channel has never been registered. This prevents firing
// multiple channel actives if the channel is deregistered and re-registered.
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
这里继续跟进doRegister方法,该方法由抽象类AbstractNioChannel完成:
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//将该channel注册到selector多路复用器
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;
}
}
}
}
在完成initAndRegister方法后,继续往下执行来到了doBind0方法,如下:
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
这里是通过绑定在Channel上的EventLoop调度一个异步任务执行channel.bind方法,这里的Channel是NioServerSocketChannel,我们跟进该方法:
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
这里调用的是ChannelPipeline的bind方法,继续跟进:
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
这里使用的是tail(AbstractHandlerContext)节点bind方法,继续进去:
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
ObjectUtil.checkNotNull(localAddress, "localAddress");
if (isNotValidPromise(promise, false)) {
return promise;
}
//我们最终找到的是HeadContext
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
//调用HeadContext的bind方法
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null, false);
}
return promise;
}
AbstractHandlerContext#invokeBind:
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
}
因为我们知道Handler是HeadContext,所以我们直接定位到HeadContext#bind方法即可:
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
unsafe.bind(localAddress, promise);
}
看到该方法内部是调用了NioMessageUnsafe#bind方法,这个在前文中有提及到,可以翻阅前文查看。这里我们直接跳到NioMessageUnsafe的父类AbstractUnsafe#bind方法:
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
这里我们其它暂时忽略,暂时只看doBind方法即可,该方法在父类中是个抽象方法,具体的实现是由其子类NioServerSocketChannel实现的:
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
这里又回到了熟悉的Java
底层API
了,并且这里还针对JVM
版本做了不同的处理,该方法将给定的地址端口进行绑定操作。
到此为止,关于ServerBootstrap的整个启动流程就完成了,接下来我们分析下客户端连接上服务端是如何处理的。
首先我们定位到NioEventLoop#run方法:
protected void run() {
int selectCnt = 0;
for (;;) {
try {
int strategy;
try {
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT:
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
if (!hasTasks()) {
strategy = select(curDeadlineNanos);
}
} finally {
// This update is just to help block unnecessary selector wakeups
// so use of lazySet is ok (no race condition)
nextWakeupNanos.lazySet(AWAKE);
}
// fall through
default:
}
} catch (IOException e) {
// If we receive an IOException here its because the Selector is messed up. Let's rebuild
// the selector and retry. https://github.com/netty/netty/issues/8566
rebuildSelector0();
selectCnt = 0;
handleLoopException(e);
continue;
}
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
if (strategy > 0) {
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
if (ranTasks || strategy > 0) {
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} catch (CancelledKeyException e) {
// Harmless exception - log anyway
if (logger.isDebugEnabled()) {
logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
selector, e);
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
这里我们暂时先看processSelectedKeys()方法:
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
跟进processSelectedKeysOptimized方法:
private void processSelectedKeysOptimized() {
//遍历所有的key
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null;
//因为attachment我们是存放着对应的channel,所以这里从attachment中获取channel
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
//处理对应的channel
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask task = (NioTask) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
这里我们直接看processSelectedKey方法:
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 {
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());
}
}
接下来我们重点放在unsafe.read方法上,由上面可知该Unsafe的实现为NioMessageUnsafe:
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 {
//处理socket
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;
}
//遍历消息然后通过ChannelPipeline触发channelRead
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 {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
在doReadMessages方法中,主要处理接收Socket的连接,如果SocketChannel不为空,则添加到buf中即可。
protected int doReadMessages(List
最终的会存储在List的readBuf中,我们客户端连接上来后会在该列表中保存一个NioSocketChannel对象。可以看到如果SocketChannel不为空的话,则会实例化一个NioSocketChannel对象,我们来看看这个对象。
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
跟进父类AbstractNioByteChannel:
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
继续跟进父类(AbstractNioChannel),这里需要注意下这里SelectionKey.OP_READ
表示监听读事件:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
ch.configureBlocking(false);
}
居然还有父类(AbstractChannel),继续跟进去看看:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
原来和之前一样,老三件了。
最后通过ChannelPipeline触发ChannelRead事件,还记得我们之前在ServerBootstrap中注册了一个ChannelInitializer中为客户端Channel注册了一个ServerBootstrapAcceptor处理器吗,而在这里就派上用场了。通过ChannelPipeline的fireChannelRead方法,最终也会调用到ServerBootstrapAcceptor对象的channelRead方法:
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//这里的msg为NioSocketChannel
final Channel child = (Channel) msg;
//配置对应的childHandler
child.pipeline().addLast(childHandler);
//配置对应的childOptions
setChannelOptions(child, childOptions, logger);
//配置对应的Attributes
setAttributes(child, childAttrs);
try {
//childGroup为之前配置的WorkerGroup,调用register方法将NioSocketChannel与EventLoop进行绑定
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);
}
}
到这里为止,Netty服务端启动以及获取客户端连接的整个流程就已经清晰了。