netty源码浅析-客户端接入

客户端接入

当客户端调用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线程组主要负责客户端连接后的读写事件。如果有分析错误的地方还请不吝指正。感谢!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值