死磕Netty源码之服务端启动源码解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/RobertoHuang/article/details/80958444

前言

本博客讲述的是Netty是如何绑定端口、启动服务。启动服务的过程中你将会了解到Netty各大核心组件

关于我:http://huangth.com

GitHub地址:https://github.com/RobertoHuang

免责声明:本系列博客并非原创,主要借鉴和抄袭闪电侠占小狼等知名博主博客。如有侵权请及时联系

服务端启动DEMO

先从一个简单的服务端启动DEMO开始,以下是一个标准的Netyy服务端代码

public final class NettyServer {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) {
                            ChannelPipeline channelPipeline = channel.pipeline();
                            channelPipeline.addLast("decoder", new StringDecoder());
                            channelPipeline.addLast("encoder", new StringEncoder());
                            channelPipeline.addLast("handler", new ServerHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

注:ServerBootstrap.childHandler()用于指定处理新连接数据的读写处理逻辑,同时ServerBootstrap还提供handler()用于指定在服务端启动过程中的一些逻辑,通常情况下我们用不着这个方法

ServerHandler代码如下:

public class ServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("channelActive");
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) {
        System.out.println("channelRegistered");
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        System.out.println("handlerAdded");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
    }

    @Override
    public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("service receive msg:" + msg);
    }
}

当有新连接接入时,控制台打印出

handlerAdded
channelRegistered
channelActive

但接收到新消息时,控制台打印出

service receive msg:xxx
channelReadComplete

本文主要分析服务端的启动过程,而新连接接入 新消息的读取会在后续章节中说明

服务端启动源码分析

ServerBootstrap是Netty为方便开发者使用而设计的一个启动类,ServerBootstrap的核心代码入口在bind(),代码如下

public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}

通过端口号创建一个InetSocketAddress,然后继续bind

public ChannelFuture bind(SocketAddress localAddress) {
    // 校验参数
    validate();
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    }
    // Channel绑定逻辑
    return doBind(localAddress);
}

validate()验证服务启动需要的必要参数,然后调用doBind()

private ChannelFuture doBind(final SocketAddress localAddress) {
    //...
    final ChannelFuture regFuture = initAndRegister();
    //...
    final Channel channel = regFuture.channel();
    //...
    doBind0(regFuture, channel, localAddress, promise);
    //...
    return promise;
}

在doBind()中我们关注两个核心方法,initAndRegister()以及doBind0()

initAndRegister

final ChannelFuture initAndRegister() {
    Channel channel = null;
    // 新建Channel
    channel = channelFactory.newChannel();
    // 初始化Channel
    init(channel);
    // 将这个Channel Register到某个对象
    ChannelFuture regFuture = config().group().register(channel);
    return regFuture;
}

新建Channel

Channel是Netty的核心概念之一,它是Netty网络通信的主体由它负责同对端进行网络通信、注册和数据操作等功能。Channel的创建是由channelFactory.newChannel()完成的,ChannelFactory接口定义如下

public interface ChannelFactory<T extends Channel> {
    T newChannel();
}

接下来我们跟踪此处的channelFactory是在何时被初始化,我们层层回溯最终发现是在这个函数中

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

在Demo程序调用.channel(NioServerSocketChannel.class)方法,所以channelFactory.newChannel()真正执行代码如下

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    private final Class<? extends T> clazz;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        }
        this.clazz = clazz;
    }

    @Override
    public T newChannel() {
        try {
            return clazz.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }
}

即在Netty服务端启动的时候通过反射的方式来创建一个NioServerSocketChannel对象,最终创建Channel相当于调用默认构造函数new出一个 NioServerSocketChannel对象,接下来我们继续跟进NioServerSocketChannel的默认构造函数

public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        // 利用SelectorProvider产生一个ServerSocketChannel对象
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException("Failed to open a server socket.", e);
    }
}

通过newSocket(DEFAULT_SELECTOR_PROVIDER)创建一条server端channel,然后进入到以下方法

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

该方法主要完成两个功能,首先是调用父类的构造方法然后初始化NioServerSocketChannelConfig属性。我们继续跟入super()

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

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    ch.configureBlocking(false);
}

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    // 设置SelectionKey.OP_ACCEPT事件
    this.readInterestOp = readInterestOp;
    // 设置ServerSocketChannel为非阻塞的
    ch.configureBlocking(false);
}

这里将前面通过provider.openServerSocketChannel()创建出来的ServerSocketChannel保存到成员变量,然后调用将该channel为非阻塞模式,这是个标准的JDK NIO编程的玩法。这里的readInterestOp即前面层层传入的SelectionKey.OP_ACCEPT,接下来继续跟进super(parent);(这里的parent其实是null,由前面写死传入)

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

在AbstractChannel的构造方法中主要是初始化了id,unsafe,pipeline属性

初始化Channel

在创建完Channel后,我们在init方法中对Channel进行初始化操作,代码如下

void init(Channel channel) throws Exception {
    // 设置option
    final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
        channel.config().setOptions(options);
    }

    // 设置attr
    final Map<AttributeKey<?>, Object> attrs = attrs0();
    synchronized (attrs) {
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
            @SuppressWarnings("unchecked")
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
            channel.attr(key).set(e.getValue());
        }
    }

    // 设置handler到pipeline上
    ChannelPipeline p = channel.pipeline();
    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
    synchronized (childOptions) {
        // 设置新接入channel的options
        currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
    }
    synchronized (childAttrs) {
        // 设置新接入channel的attrs
        currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
    }

    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(Channel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();
            // 这里的handler()返回的就是.handler()所设置的值
            ChannelHandler handler = config.handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }

            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    // p.addLast()向serverChannel的流水线处理器中加入了一个ServerBootstrapAcceptor
                    // 从名字上就可以看出来这是一个接入器,专门接受新请求,把新的请求扔给某个事件循环器
                    pipeline.addLast(new ServerBootstrapAcceptor(currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

以上代码主要完成如下功能:先调用options0()以及attrs0()获取到服务器启动时设置的一些参数,然后将得到的options和attrs注入到channelConfig或者channel中;然后在当前Channel的pipeline中添加了一个ChannelInitializer,在ChannelInitializer中往pipeline中添加了一个handler,并通过NioEventLoop.execute()方法往pipeline中添加了一个ServerBootstrapAcceptor(请求接入器),此处的NioEventLoop.execute()方法为Netty Reactor线程执行的入口,关于Netty Reactor线程我们将在下一篇博客中介绍。我们总结一下发现代码执行到这里Netty并未真正启动服务,只是初始化了一些基本的配置和属性,以及在pipeline上加入了一个接入器用来专门接受新连接

完成Channel注册

完成Channel注册的代码如下

ChannelFuture regFuture = config().group().register(channel);

它调用到MultithreadEventLoopGroup中的register方法

@Override
public ChannelFuture register(Channel channel) {
    // 调用了NioEvenLoop对象中的register方法
    // EventLoopGroup extends SingleThreadEventLoop
    return next().register(channel);
}   

在next方法中返回一个EventLoop对象,每一个EventLoop都与一个selector绑定,在之前的代码中EventLoop中的Selector一直没有任何Channel注册,所以每次select操作都是空,但从这行代码开始这个selector中开始有Channel注册

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;
}

这里可以看到register操作是委托给Channel中的Unsafe对象来执行的,这里的Unsafe对象对上文稍有印象的同学应该能知道这个就是创建NioServerSocketChannel的时候创建的Unsafe对象,继续跟进Unsafe对象的register方法

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // ...
    AbstractChannel.this.eventLoop = eventLoop;
    // ...
    register0(promise);
}

先将EventLoop事件循环器绑定到该NioServerSocketChannel上,然后调用register0()代码如下

private void register0(ChannelPromise promise) {
    try {
        boolean firstRegistration = neverRegistered;
        doRegister();
        neverRegistered = false;
        registered = true;

        pipeline.invokeHandlerAddedIfNeeded();

        safeSetSuccess(promise);
        pipeline.fireChannelRegistered();
        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    } catch (Throwable t) {
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
    }
}

这一段其实也很清晰,先调用doRegister()进行注册

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            // ...
        }
    }
}

在这里我们终于看到JDK底层的Channel注册到Selector的过程,但是这里的OPS为0即不关心任何事件,而我们期望OPS的值为SelectionKey.OP_ACCEPT,所以到了这里代码还没有结束。在执行完Channel注册后接着执行了几个pipeline相关的方法,我们后面详细剖析pipeline的时候再讲,然后我们回到doBind0方法中

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());
            }
        }
    });
}

在dobind0()方法中通过EventLoop执行一个任务,接下来我们进入到channel.bind()方法

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return pipeline.bind(localAddress, promise);
}

public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return tail.bind(localAddress, promise);
}

关于Pipeline相关的内容将在后续博客中介绍,当前一个比较好的方式就是Debug单步进入。最后我们来到了如下区域

public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
    unsafe.bind(localAddress, promise);
}

这里的unsafe就是前面提到的AbstractUnsafe, 准确点应该是NioMessageUnsafe

@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
    // ...
    boolean wasActive = isActive();
    // ...
    doBind(localAddress);

    if (!wasActive && isActive()) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                pipeline.fireChannelActive();
            }
        });
    }
    safeSetSuccess(promise);
}

在doBind方法中完成绑定操作,代码如下

protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

最终调用到了JDK里面的bind方法真正进行了端口的绑定。按照正常流程我们前面已经分析到isActive()方法返回false,进入到 doBind()之后如果channel被激活了,就发起pipeline.fireChannelActive()调用。接着我们跟进pipeline.channelActive方法

public void channelActive(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelActive();
    readIfIsAutoRead();
}

pipeline.channelActive会逐一调用pipeline中每一个节点的channelActive方法,并且在HeadContext中调用了readIfIsAutoRead

private void readIfIsAutoRead() {
    if (channel.config().isAutoRead()) {
        channel.read();
    }
}

最终这个方法会调用到AbstractNioChannel的doBeginRead方法

protected void doBeginRead() throws Exception {
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

在最后一行中的readInterestOp即在上文中提到的SelectionKey.OP_ACCEPT,至此完成了Channel对ACCEPT事件的注册过程

总结

到目前为止我们看到的代码相当于传统NIO编程中的如下代码

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); => 创建NioServerSocketChannel
serverSocketChannel.configureBlocking(false); => AbstractNioChannel中ch.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress("localhost", 8888)); => NioServerSocketChannel.doBind()
Selector selector = Selector.open(); => NioEventLoop.openSelector()
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT) => AbstractNioChannel.doBeginRead()

服务端启动完成的主要功能为创建一个Channel,并且将Channel注册到NioEventLoop的Selector上

猜你喜欢

转载自blog.csdn.net/RobertoHuang/article/details/80958444