Netty4.0源码解析:NioServerSocketChannel

一、引言

Netty的Channel在JDK NIO的Channel基础上做了一层封装,提供了更多的功能。Netty的中的Channel实现类主要有:NioServerSocketChannel(用于服务端非阻塞地接收TCP连接)、NioSocketChannel(用于维持非阻塞的TCP连接)、NioDatagramChannel(用于非阻塞地处理UDP连接)、OioServerSocketChannel(用于服务端阻塞地接收TCP连接)、OioSocketChannel(用于阻塞地接收TCP连接)、OioDatagramChannel(用于阻塞地处理UDP连接):
在这里插入图片描述

一个EventLoop一般持有多个Channel,每个EventLoop持有一个对应的线程,有这个线程负责处理这些Channel发出的事件。

本篇文章我们先从服务端的NioServerSocketChannel开始分析:


二、初始化过程

NioServerSocketChannel在JDK的ServerSocketChannel的基础上做了一层封装。在NioServerSocketChannel的初始化过程中,可以简要地分为三步:
1、实例化NioServerSocketChannel
2、完成NioServerSocketChannel的管道初始化的第一步骤(向管道添加初始的ChannelHandler)
3、将NioServerSocketChannel注册到NioEventLoopGroup,在此期间完成管道初始化的第二步骤(比如执行ChannelInitializer的handlerAdd方法)
4、绑定端口,开始接受客户端连接。

我们在ServerBootstrap进行引导的过程中,需要调用channel方法指定一个ServerChannel实现类的Class对象,channel方法随即会生成一个ChannelFactory工厂于ServerBootstrap实例中。NioServerSocketChannel的实例化在ServerBootstrap的bind方法中完成。bind方法调用的doBind方法中的initAndRegister方法中,会通过ChannelFactory实例构造出一个NioServerSocketChannel:

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
    	//通过ChannelFactory构造一个Channel实例
        channel = channelFactory().newChannel();
        init(channel);
    } catch (Throwable t) {
        if (channel != null) {
            channel.unsafe().closeForcibly();
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
    }
	//获取bossGroup实例并调用register方法绑定
    ChannelFuture regFuture = group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

通过ChannelFactory构造的Channel都是通过无参构造方法构造的,我们来分析NioServerSocketChannel的构造方法:

public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

这里通过静态newSocket方法构造了一个JDK的ServerSocketChannel:

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException("Failed to open a server socket.", e);
    }
}

这里通过默认的SelectorProvider实例通过调用它的openServerSocketChannel构造了一个JDK的ServerSocketChannel实例,接着调用另外的重载的构造方法

public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

NioServerSocketChannelConfig是NioServerSocketChannel的内部类,保存了以下内容:NioServerSocketChannel(this)、和刚刚构造的ServerSocketChannel通过socket方法获取的ServerSocket实例、引导过程通过options方法设置的参数。

NioServerSocketChannel父类AbstractNioMessageChannel的构造方法:

protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent, ch, readInterestOp);
}

AbstractNioMessageChannel父类AbstractNioChannel构造方法:

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch; //传入JDK ServerSocketChannel
    this.readInterestOp = readInterestOp; //感兴趣通道事件为OP_ACCEPT
    try { //将ServerSocketChannel设为非阻塞
        ch.configureBlocking(false);
    } catch (IOException e) { //如果抛出异常那么关闭ServerSocketChannel
        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);
    }
}

AbstractNioChannel父类AbstractChannel构造方法:

protected AbstractChannel(Channel parent) {
    this.parent = parent; //ServerSocketChannel不存在父Channel
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

ServerSocketChannel不存在父Channel,所以parent为null,只有SocketChannel存在(其parent为ServerSocketChannel实例)。
接着通过newUnsafe方法构造一个Channel.Unsafe接口实现类。在客户端引导过程已经提到过,Unsafe是Netty到JDK NIO的桥梁,Unsafe接口定义了以下方法:

方法签名作用
SocketAddress localAddress()获取该Channel的本地地址
SocketAddress remoteAddress()获取该Channel连接地址
void register(EventLoop, ChannelPromise)向EventLoop注册这个Channel,并通知指定的ChannelPromise
void bind(SocketAddress, ChannelPromise)指定一个本地地址,将Channel绑定在这个地址,并通知指定的ChannelPromise
void connect(SocketAddress, SocketAddress, ChannelPromise)通过本地地址向指定的地址发起连接,并通知ChannelPromise
void disconnect(ChannelPromise)取消连接,并通知指定的ChannelPromise
void close(ChannelPromise)关闭Channel通道,并通知指定的ChannelPromise
void closeForcibly()立刻关闭Channel通道,不通知ChannelPromise
void deregister(ChannelPromise)将这个Channel从EventLoop取消注册,并通知指定的ChannelPromise
void beginRead()写入缓冲区,以便进站处理器能够读到进站数据
void write(Object, ChannelPromise)向缓冲区中写入数据,并通知指定的ChannelPromise
void flush()输出调用write方法写入的缓冲区数据
ChannelPromise voidPromise()返回这个Channel的特殊ChannelPromise(Void Promise),用于将ChannelPromise作为参数但不希望得到通知的操作。
ChannelOutboundBuffer outboundBuffer()获取出站缓冲区

对于NioServerSocketChannel,其newUnsafe方法实现在父类AbstractNioMessageChannel中:

@Override
protected AbstractNioUnsafe newUnsafe() {
    return new NioMessageUnsafe();
}

NioMessageUnsafe继承了AbstractNioUnsafe,重写了read方法,用于处理OP_ACCPET事件。关于事件处理细节可以参考我的博客:https://blog.csdn.net/abc123lzf/article/details/83313530

newChannelPipeline方法则是比较简单,直接构造一个DefaultChannelPipeline就完了。

回到ServerBootstrap:当initAndRegister方法执行结束后,init方法随即会被调用并传入刚才构造的ServerSocketChannel实例作为参数:

@Override
void init(Channel channel) throws Exception {
	//将通过option方法指定参数加入到Map中
    final Map<ChannelOption<?>, Object> options = options();
    synchronized (options) {
        setChannelOptions(channel, options, logger);
    }
    //将初始化属性传入到Map中
    final Map<AttributeKey<?>, Object> attrs = attrs();
    synchronized (attrs) {
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
            @SuppressWarnings("unchecked")
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
            channel.attr(key).set(e.getValue());
        }
    }
    ChannelPipeline p = channel.pipeline();
    //获取workGroup、childGroup指定的ChannelHandler
    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
    synchronized (childOptions) {
        currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
    }
    synchronized (childAttrs) {
        currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
    }
	//向管道尾部添加一个ChannelInitializer实例
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(final Channel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = handler();
            //添加用户自定义的bossGroup的ChannelHandler
            if (handler != null)
                pipeline.addLast(handler);
			//向管道尾部添加一个ServerBootstrapAcceptor实例
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, 
                    		currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

init方法将引导过程通过options方法设置的参数导入到NioServerSocketChannel的ChannelConfig中,然后再将初始属性导入到NioServerSocketChannel间接父类DefaultAttributeMap中。

完成上述步骤后,向ServerSocketChannel所属的管道尾部添加一个ChannelInitializer实例,它重写了initChannel方法,并指定向管道添加用户自定义的ChannelHandler(通过handler方法指定的),然后再异步添加一个ServerBootstrapAcceptor实例,用于将接收到的客户端连接对应的NioSocketChannel转交给workGroup进行管理。

前几篇博客我们已经提到过,ChannelInitializer在完成自己的工作(执行完initChannel方法)后会将自己从管道中移除。但是就目前而言还仅仅只是完成了管道初始化的第一步骤,因为ChannelInitializer的handlerAdd方法还尚未调用。

init方法执行完毕后,就会将这个ServerSocketChannel注册到bossGroup中,最终是通过AbstractUnsafe的register方法进行注册:

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    if (eventLoop == null)
        throw new NullPointerException("eventLoop");
    if (isRegistered()) { //不允许重复注册或者同一个Channel注册到不同的EventLoop中
        promise.setFailure(new IllegalStateException("registered to an event loop already"));
        return;
    }
    if (!isCompatible(eventLoop)) { //如果不能注册到这种类型的EventLoop中
        promise.setFailure(new IllegalStateException("incompatible event loop type: " +
        		eventLoop.getClass().getName()));
        return;
    }
    AbstractChannel.this.eventLoop = eventLoop;
    //如果当前线程就是eventLoop所属的线程,那么直接执行register0方法,否则异步执行
    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);
        }
    }
}

register方法在进行一系列参数检查和状态检查后,继而会执行register0方法:

private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !ensureOpen(promise))
            return;
        boolean firstRegistration = neverRegistered; //默认为true
        doRegister(); //注册到Selector中
        neverRegistered = false;
        registered = true;
        pipeline.invokeHandlerAddedIfNeeded();
        safeSetSuccess(promise);
        //向管道发出Channel注册事件
        pipeline.fireChannelRegistered();
        if (isActive()) { //如果Channel是可用的
            if (firstRegistration) {
            	//向管道发出Channel可用事件
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    } catch (Throwable t) {
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
    }
}

register0方法首先调用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) {
                eventLoop().selectNow();
                selected = true;
            } else {
                throw e;
            }
        }
    }
}

doRegister将ServerSocketChannel注册到了EventLoop的Selector上,并将附加对象设为this。doRegister方法执行结束后,就可以认为Channel已经注册到了EventLoop中,因为现在这个Channel持有的JDK Channel已经被EventLoop持有的Selector管理。
随后,就会调用管道对象的invokeHandlerAddedIfNeeded方法:

final void invokeHandlerAddedIfNeeded() {
    assert channel.eventLoop().inEventLoop();
    if (firstRegistration) {
        firstRegistration = false;
        callHandlerAddedForAllHandlers();
    }
}

invokeHandlerAddedIfNeeded此时就会调用callHandlerAddedForAllHandlers执行回调任务。

让我们回顾下服务端的引导过程,当调用addLast方法将ChannelInitializer添加到管道中后,如果管道检测到这是它第一次调用addLast方法,并不会马上执行ChannelInitializer的handlerAdd方法去执行我们重写的方法,而是注册了一个回调任务,并把这个回调任务添加到了DefaultChannelPipeline的成员变量pendingHandlerCallbackHead中,如果有多个回调任务,那么它可以通过它的成员变量next去维护一个单向链表。

了解这些后,我们再来看callHandlerAddedForAllHandlers这个方法的实现:

private void callHandlerAddedForAllHandlers() {
    final PendingHandlerCallback pendingHandlerCallbackHead;
    synchronized (this) {
        assert !registered;
        registered = true; 
        pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
        this.pendingHandlerCallbackHead = null;
    }
    //遍历该链表,依次执行回调任务
    PendingHandlerCallback task = pendingHandlerCallbackHead;
    while (task != null) {
        task.execute();
        task = task.next;
    }
}

这样,我们指定的ChannelInitializer的handlerAdd方法随即会被调用,管道初始化的第二步骤也就随之完成。

回到register0方法:
接下来会依次向管道传递ChannelRegister、ChannelActive事件。传递完成后,NioServerSocketChannel开始进入端口绑定过程。
回到ServerBootstrap的doBind方法,doBind方法会开始进行端口的绑定过程,最终会调用到NioServerSocketChannel的doBind方法:

@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) { //如果JDK版本在1.7以上
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

这里直接调用到了JDK ServerSocketChannel的bind方法直接绑定指定的端口,并指定了最大连接数(默认为128),可以通过调用options并传入ChannelOption.SO_BACKLOG,然后指定一个最大连接数。当连接数超出后,客户端的连接请求会被阻塞。

至此,NioServerSocketChannel初始化过程完成。


三、事件处理

初始化过程完成后,此时的NioServerSocketChannel已经被EventLoop的Selector所管理,Selector则由EventLoop所属的线程进行轮询。这个线程运行NioEventLoop的run方法,通过一个无限循环不停地处理IO事件和一般任务。

当有客户端发起连接时,ServerSocketChannel会发出OP_ACCEPT事件,就会通过Unsafe的read方法(实现在AbstractNioMessageUnsafe)处理这个事件:

@Override
public void read() {
    assert eventLoop().inEventLoop();
    final ChannelConfig config = config();
    
    if (!config.isAutoRead() && !isReadPending()) {
        // ChannelConfig.setAutoRead(false) was called in the meantime
    	//从事件集中移除这个事件
        removeReadOp();
        return;
    }
    //获取每个循环读取消息的最大字节数
    final int maxMessagesPerRead = config.getMaxMessagesPerRead();
    final ChannelPipeline pipeline = pipeline();
    boolean closed = false;
    Throwable exception = null;
    try {
        try {
            for (;;) {
            	//将数据读到readBuf中,返回读取到的字节数
                int localRead = doReadMessages(readBuf);
                if (localRead == 0)
                    break;
                if (localRead < 0) {
                    closed = true;
                    break;
                }
                if (!config.isAutoRead())
                    break;
                //如果readBuf的长度大于maxMessagesPerRead,退出循环
                if (readBuf.size() >= maxMessagesPerRead)
                    break;
            }
        } catch (Throwable t) {
            exception = t;
        }
        setReadPending(false);
        //获取读取到的ByteBuf数量,并通过循环将这些ByteBuf传递给ChannelInboundHandler
        int size = readBuf.size();
        for (int i = 0; i < size; i ++) {
            pipeline.fireChannelRead(readBuf.get(i));
        }
        //清除这些读取到的ByteBuf,并调用ChannelInboundHandler的fireChannelReadComplete方法
        readBuf.clear();
        pipeline.fireChannelReadComplete();

        //如果发生异常,调用ChannelInboundHandler的fireExceptionCaught方法
        if (exception != null) {
            closed = closeOnReadError(exception);
            pipeline.fireExceptionCaught(exception);
        }

        if (closed)
            if (isOpen())
                close(voidPromise());
    } finally {
    	//再次检查这个事件有没有从事件集中去除
        if (!config.isAutoRead() && !isReadPending()) {
            removeReadOp();
        }
    }
}

关于NioEventLoop事件处理细节可以参考我的博客:https://blog.csdn.net/abc123lzf/article/details/83313530 ,这里就不再赘述了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值