netty分析

目录

服务端启动

步骤一:创建服务端Channel

步骤二:初始化服务端Channel

init()方法

步骤三:注册selector

步骤四:端口绑定


netty整体架构

Netty与Socket的对应关系

  • NioEventLoop --> Thread

  • Channel --> Socket

  • ByteBuf --> IO Bytes

  • Pipeline --> Logic Chain

  • ChannelHandler --> Logic

服务端启动

问题

  1. 服务端的socket在哪里初始化?
  2. 在哪里accept连接?

四步骤

  1. 创建服务端Channel:调用JDK底层API创建JDK的channel,然后netty将其包装成自己的channel,同时创建一些基本组件绑定到此channel上;
  2. 初始化服务端Channel:初始化基本属性、添加逻辑处理器
  3. 注册selector:netty将底层的channel注册到事件轮询器selector上,并把服务端Channel作为一个attachment绑定在对应的JDK底层的服务端Channel。
  4. 端口绑定:调用JDK底层的API实现对端口的监听。

核心路径

newChannel() --> init() --> register() --> doBind()

调用newChannel()创建服务端的Channel,实际上是调用jdk底层API来创建一个JDK的Channel,然后netty将其包装成自己的服务端Channel,同时创建一些基本的属性绑定到此Channel上,比如pipeline。接着调用init()初始化服务端Channel,最重要的过程是为Channel添加一个连接处理器。随后调用register()方法注册selector,netty将jdk底层的Channel注册到事件轮询器selector上,并把服务端的Channel作为一个attachment绑定到对应的JDK底层的Channel。最后,调用doBind()方法调用JDK底层的API实现对本地端口的监听,netty会向selector注册一个OP_ACCEPT事件,这样netty就可以接受新的连接了。

步骤一:创建服务端Channel

bind() [用户代码入口] --> initAndRegister()[初始化并注册] --> [创建服务端channel]

从bind()方法开始,

  • bind()方法调用doBind()方法
  • doBind()方法内调用了initAndRegister() 
  • initAndRegister() 内调用了channelFactory.newChannel()

bind()方法调用doBind()方法

public ChannelFuture bind() {
    validate();
    SocketAddress localAddress = this.localAddress;
    if (localAddress == null) {
        throw new IllegalStateException("localAddress not set");
    }
    return doBind(localAddress);
}

doBind()方法内调用了initAndRegister() 

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }
    ......
}

 initAndRegister() 内调用了channelFactory.newChannel()

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
        ......
}

 到这里,需要注意channelFactory是如何创建channel的?

在源代码中,b.group(group).channel(NioServerSocketChannel.class)。从代码可以看出,通过指定的channelClass由反射的方式创建channel。

也就是在channel()方法中,通过反射的方式调用NioServerSocketChannel类的构造函数创建此类

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

NioServerSocketChannel类的构造函数做的工作

  • newSocket() [通过JDK来创建底层JDK Channel]
  • NioServerSocketChannelConfig()  [tcp参数配置类]
  • 调用父类AbstractNioChannel -- 分为两步(1、configureBlocking(false)--设置阻塞模式;2、AbstractChannel()--设置id、unsafe、pipeline)

newSocket() [通过JDK来创建底层JDK Channel]

public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openServerSocketChannel();  //jdk底层方法
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }

NioServerSocketChannelConfig()  [tcp参数配置类]

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

调用父类AbstractNioChannel

NioServerSocketChannel.super() --> AbstractNioMessageChannel.super() --> AbstractNioChannel()

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            ch.configureBlocking(false);
        } catch (IOException e) {
            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);
        }
}

不管是服务端还是客户端Channel,均继承 AbstractChannel(),相应的会有三个属性。

AbstractNioChannel.super()

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

步骤二:初始化服务端Channel

在步骤一中,我们看到了initAndRegister()方法,里面有两步:newChannel() -- 创建Channel;init() -- 初始化Channel

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
        ......
}

init()方法

  • setChannelOptions、attribute设置用户自定义属性,将其保存起来
  • 设置 childOptions和childAttrs,同上
  • config handler [配置服务端pipeline]
  • 默认添加ServerBootstrapAcceptor()

setChannelOptions、attribute设置用户自定义属性,将其保存起来

void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

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

设置 childOptions和childAttrs

synchronized (childOptions) {
    currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
}
synchronized (childAttrs) {
    currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}

config handler [配置服务端pipeline]

默认添加ServerBootstrapAcceptor()连接处理器,将其他用户自定义属性传进去

     p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();  //拿到用户自定义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));
                    }
                });
            }
        });

步骤三:注册selector

AbstractChannel.register()  [入口]

  • this.eventLoop = eventLoop -- [绑定线程]
  • register0(promise) -- [实际注册]

register0(promise)

  • doRegister() -- [调用jdk底层注册]
  • invokeHandlerAddedIfNeeded() 
  • fireChannelRegistered() -- [传播事件]

后两步invoke...和fire...会触发-- [激活主程序中的handler()中的重写方法]

从initAndRegister()方法开始,调用register(channel)方法
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
        ......
        }

        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

 其中的register()方法会调用到AbstractChannel的register()方法

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

            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 {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                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()方法

protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                0--不关心任何事件,只是把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;
                }
            }
        }
    }

步骤四:端口绑定

AbstractChannel.bind()  [入口]

  • doBind() -- javaChannel().bind()  [jdk底层绑定]
  •  pipeline.fireChannelActive() [传播事件] -- 触发一个ChannelRead事件,对服务端Channel来说表示可以读一个新的连接

从AbstractChannel.bind()方法中的doBind()方法开始

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            assertEventLoop();
            ......
            boolean wasActive = isActive();   //在服务端绑定之前是false
            try {
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }
            //绑定之后是true
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

走进NioServerSocketChannel.doBind()方法

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

猜你喜欢

转载自blog.csdn.net/qq_40722284/article/details/91887504
今日推荐