在上一小节的讲解中,我们对doBind方法中的initAndRegister()方法进行了底层源码的分析,而在这小节中主要是对doBind方法中另一个比较重要的方法调用做讲解—doBind0()方法,这个方法做了实际的绑定工作:
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
//这里又用到了eventLoop.execute方法,这个方法前面讲解了,就不再赘述了
//eventLoop().execute
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
//实际调用了AbstractChannel的bind方法
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
//bind是出站事件,所以应该有出站handler处理,从tail向前找到第一个出站handler
//而此时pipeline中只有三个handler:HeadContext(入/出)->ServerBootstrapAcceptor(入)->TailContext(入)
return pipeline.bind(localAddress, promise);
}
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
//来到了tail.bind,在这个方法会向前找到最先找到的出站handler
return tail.bind(localAddress, promise);
}
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
//findContextOutbound这个方法就是查找到最近的出站handler(context)
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
}
private AbstractChannelHandlerContext findContextOutbound() {
//思路就是以当前handler为起点向前遍历,判断是否为outbound,不是则继续向前,直到找到一个outbound为止,这个很好理解。
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
因为主channel的pipeline中只有HeadContext能够处理出站事件,最终肯定是来到HeadContext:
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
//这里是调用的AbstractUnsafe的bind方法,这个之前讲过一次,在Unsafe系列源码中,这里就再详细说说
unsafe.bind(localAddress, promise);
}
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
this.assertEventLoop();
if (promise.setUncancellable() && this.ensureOpen(promise)) {
if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
AbstractChannel.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.");
}
//先获取绑定前的Channel状态是否为active激活状态
boolean wasActive = AbstractChannel.this.isActive();
try {
//doBind方法是一个抽象方法,在NioSocketChannel和NioServerSocketChannel中有不同的实现
AbstractChannel.this.doBind(localAddress);
} catch (Throwable var5) {
this.safeSetFailure(promise, var5);
this.closeIfClosed();
return;
}
//这里表示如果绑定前为未激活,绑定后为激活,说明Channel是在此次绑定过程中激活的
//那么就将fireChannelActive交给EventLoop中的线程执行
if (!wasActive && AbstractChannel.this.isActive()) {
this.invokeLater(new Runnable() {
public void run() {
//这里会传递ChannelActive事件,这是一个入站事件,所以从Head开始
AbstractChannel.this.pipeline.fireChannelActive();
}
});
}
this.safeSetSuccess(promise);
}
}
先来看看两种doBind方法的实现,然后在看看invokeLater方法。
NioServerSocketChannel:
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
//可以设置backlog参数
this.javaChannel().bind(localAddress, this.config.getBacklog());
} else {
this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
}
}
NioSocketChannel:
private void doBind0(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
SocketUtils.bind(this.javaChannel(), localAddress);
} else {
SocketUtils.bind(this.javaChannel().socket(), localAddress);
}
}
接下来看invokeLater,主要是将fireChannelActive打包成Runnable交给EventLoop执行
private void invokeLater(Runnable task) {
try {
//这个eventLoop().execute(task)看不过最少三五遍了吧,前面有详细讲解过,不再赘述
AbstractChannel.this.eventLoop().execute(task);
} catch (RejectedExecutionException var3) {
AbstractChannel.logger.warn("Can't invoke task later as EventLoop rejected it", var3);
}
}
AbstractUnsafe的bind方法中执行完了doBind方法后会传递channelActive事件,这个事件是入站事件,HeadContext(入/出)->ServerBootstrapAcceptor(入)->TailContext(入)这三个handler都是处理入站事件的,不过ServerBootstrapAcceptor 和 TailContext 的 channelActive 方法都没有做任何实质性的事情,只有HeadContext 有实质性代码
先来看看是如何选择到headContext:
public final ChannelPipeline fireChannelActive() {
//这里可以看到,将head作为参数传入,因为入站方法默认从头开始找起,进入invokeChannelActive方法
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
//next是headContext
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
//executor.execute这个多次出现,不再赘述
executor.execute(new Runnable() {
@Override
public void run() {
//next实际上是参数HeadContext,因为Headcontext就是第一个入站处理器,所以直接调用Headcontext.invokeChannelActive
next.invokeChannelActive();
}
});
}
}
private void invokeChannelActive() {
if (invokeHandler()) {
try {
//这个handler就是HeadContext,接下来就看channelActive事件是如何处理的
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
接下来就看到这个事件在pipeline中各个handler中的传递和处理:ServerBootstrapAcceptor 和 TailContext 的 channelActive 方法都没有做任何实质性的事情,只有HeadContext 有实质性代码
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//这个是往后传递,也就是ServerBootstrapAcceptor
ctx.fireChannelActive();
//向后传递后,执行readIfIsAutoRead方法
readIfIsAutoRead();
}
//ServerBootstrapAcceptor 父类ChannelInboundHandlerAdapter中的channelActive,只是简单的先后传递
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
}
//TailContext的channelActive,不做处理逻辑
public void channelActive(ChannelHandlerContext ctx) throws Exception {
onUnhandledInboundChannelActive();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
//AbstractChannel 的 read 方法
channel.read();
}
}
public Channel read() {
//调用了 DefaultChannelPipeline 的 read 方法
pipeline.read();
return this;
}
public final ChannelPipeline read() {
//AbstractChannelHandlerContext.read方法
tail.read();
return this;
}
public ChannelHandlerContext read() {
//read是一个Outbound事件,因此findContextOutbound()会以tail为起点向前一个个查找出站处理器,前面有看过这个方法就不赘述了
//因为HeadContext(入/出)->ServerBootstrapAcceptor(入)->TailContext(入)中只有HeadContext是出站处理器,所以这个next只能是HeadContext
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
//这里判断当前线程与eventLoop中的线程是否为同一个,如果是则直接执行next.invokeRead,如果不是则交给eventLoop执行
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Runnable task = next.invokeReadTask;
if (task == null) {
next.invokeReadTask = task = new Runnable() {
@Override
public void run() {
next.invokeRead();
}
};
}
executor.execute(task);
}
return this;
}
private void invokeRead() {
if (invokeHandler()) {
try {
//这个handler实际是HeadContext,所以来到HeadContext中的read方法
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
public void read(ChannelHandlerContext ctx) {
//AbstractUnsafe的beginRead方法
unsafe.beginRead();
}
public final void beginRead() {
//判断当前的线程是否为eventLoop中的线程
assertEventLoop();
if (!isActive()) {
return;
}
try {
//最重要的就是doBeginRead方法,这个方法在讲解Channel系列源码的AbstractNioChannel中详细讲到过,
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
protected void doBeginRead() throws Exception {
SelectionKey selectionKey = this.selectionKey;
//判断是否有效
if (selectionKey.isValid()) {
this.readPending = true;
int interestOps = selectionKey.interestOps();
//条件为true则表示当前channel还没有设置对readInterestOp所表示的事件感兴趣
if ((interestOps & this.readInterestOp) == 0) {
//这里就是真正的对readInterestOp表示的事件进行监听,可以看出这些地方都是用位运算,效率高
//这里用到的readInterestOp是哪里设置到记得不?是在构造函数中设置进来的
//如果是NioServerSocketChannel则对accept事件感兴趣,如果是NioSocketChannel则对read读事件感兴趣
selectionKey.interestOps(interestOps | this.readInterestOp);
}
}
}
到了这里,doBind方法基本就完成了,也就代表着整个服务器启动流程已经结束了。整个流程下来,是不是很多的之前讲到的东西基本都串起来了,从Channel到pipeline,从pipeline到HandlerContext,从HandlerContext到Handler,从Handler到Unsafe,全部组件都用自己的任务,相互协作完成了整个启动流程。接下来一下节就是讲讲Netty在处理事件上的整体流程。