`
airu
  • 浏览: 267304 次
  • 性别: Icon_minigender_1
  • 来自: 云南
社区版块
存档分类
最新评论

netty 源码之Bootstrap

 
阅读更多
Bootstrap 以及 ServerBootstrap类都继承自 AbstractBootstrap
这个抽象类很有趣,请看
public abstract class AbstractBootstrap<B extends AbstractBootstrap<?>> {
...
}

泛型中的类是自身的子类。其实如果明白这个类的作用,就知道为什么要这么写了。
见doc:
AbstractBootstrap is a helper class that makes it easy to bootstrap a  Channel. It support method-chaining to provide an easy way to configure the AbstractBootstrap
也就是说,这是一个帮助类,为了更方便启用Channel,为了方便,支持了method-chainning 也就是说函数都返回一个对自身的引用。这样我们就可以使用
instance.get().put(3).add(4)之类的写法,在NIO中很常见。

下面我们看看 AbstractBootstrap有些什么。
先看 成员变量,
public abstract class AbstractBootstrap<B extends AbstractBootstrap<?>> {
    private EventLoopGroup group;
    private ChannelFactory factory;
    private SocketAddress localAddress;
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
    private ChannelHandler handler;
}


一个事件组。netty是事件驱动的,事件组应该是各种事件的容器(猜想,因为还没看,也暂时不用在意)
一个Channel工厂,这自然是用来生产Channel的(这里应该有各种管道。比如服务端的和客户端的就不一样)
SocketAddress是本地地址
options 是这个管道的属性集合
attrs 是属性键集合(这个搞不懂,先放下)
ChannelHandler 用于处理管道的各种事件吧。
猜想到此为止。我们看看具体的功能函数吧。

1, public B group(EventLoopGroup group)
这个函数简单,设置group,注意如果传入参数为空,或者原先的group不为空都要抛出异常。然后,返回自身 ,注意泛型 B

2, public B channel(Class<? extends Channel> channelClass)
这个类用来创建一个Channel实例,使用了ChannelFactory。使用工厂模式推迟具体实例创建到子类中。这里可以看做一个工厂设置

3, public B channelFactory(ChannelFactory factory)
2调用了3,

4,public B localAddress(SocketAddress localAddress)
绑定本地地址

5, public B localAddress(int port)
同样绑定本地地址

6, public B localAddress(String host, int port)
同4,5

7, public  B localAddress(InetAddress host, int port)
通4,5,6

8, public <T> B option(ChannelOption<T> option, T value)
实例化Channel时的参数。如果 value为null,这个option被remove掉。

9, public <T> B attr(Attribute<T> key, T value)
实例化Channel后Channel的属性value为null时,attr被remove掉。

10, public void shutdown()
这里关闭的是EventLoopGroup

11, protected void validate()
这里变protected了,用于同类的参数验证。子类如果复写,需要使用super先调用父类

12,protected final void validate(ChannelFuture future)
验证了future不为null,然后调用11, 不可以被复写

13,public ChannelFuture bind()
创建一个Channel并且绑定

14,public B handler(ChannelHandler handler)
handler不能为null,设置handler

15,public static boolean ensureOpen(ChannelFuture future)
确保future中的channel为open状态,否则futrue设置为失败ChannelException

16,public abstract ChannelFuture bind(ChannelFuture future)
把Channel绑定到给定的ChannelFactory上。抽象方法,意味着需要具体的Channel

之后的方法都类似于get方法。不在一一列举。
综上所观,这个类就是一个配置类。
我们在看看具体的子类。
首先
Bootstrap类增加了InternalLogger 和SocketAddress(remoteAddress)
在抽象类中没有实现的 bind函数,这里也实现了。
  @Override
    public ChannelFuture bind(ChannelFuture future) {
        validate(future);
        if (localAddress() == null) {
            throw new IllegalStateException("localAddress not set");
        }

        try {
            init(future.channel());
        } catch (Throwable t) {
            future.setFailure(t);
            return future;
        }

        if (!ensureOpen(future)) {
            return future;
        }

        return future.channel().bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    }


这段代码基本就是原始的netty的客户端使用标准。
首先,validate验证futrue,factory和group,再验证localAddress
接下来就是利用future的特性初始化channel,如果channel没有打开,那么ensureOpen是已经设置了future的失败,直接返回future,如果channel成功打开,那么绑定本地地址并且添加一个失败关闭的ChannelFutrueListener
这里,关键的是init(Channel)函数了。
init函数首先检查Channel是否是激活的,是否是注册的,是否是打开的。
然后再Channel的pipeline中加入handler,并且添加ChannelOption参数和Attribute属性
我们也看到,Channel已经不是NIO中的Channel了,她是netty的Channel,每个Channel都有ChannelPipeline,也有ChannelConfig这样的东西。记住这些,我们后面会去看他们是如何工作的。
最后,仔细看 group().register(channel).syncUninterruptibly();
这句使得我们的channel的register变成同步不可中断的。这里的注册应该是让我们的Channel成为EventLoopGroup的会员。

与bind类似的函数就是connect了。客户端的一个动作就是连接服务端。
那么connect也是和bind类似,不过要注意,bind是bind本地地址,而connect则需要的是远程地址,所以检查的时候不一样,而且connect的时候可以没有本地绑定哦。
如 future.channel().connect(remoteAddress,futrue)
同样给future设置一个监听器,当连接失败的时候关闭Channel

客户端的validate,多了一个handler的validate,如果没有会报IllegalStateException,所以也要设置handler

最后,看看 public Bootstrap channel(Class<? extends Channel> channelClass)
函数,这个函数用于设置不同的ChannelFactory来创建不同的Channel
这里,如果channelClass是AioSocketChannel,那么我们就使用AioSocketChannelFactory,否则就是默认的。

这里还内嵌了AioSocketChannelFactory,实现很简单
 private final class AioSocketChannelFactory implements ChannelFactory {
        @Override
        public Channel newChannel() {
            return new AioSocketChannel((AioEventLoopGroup) group());
        }

        @Override
        public String toString() {
            return AioSocketChannel.class.getSimpleName() + ".class";
        }
    }

所以可以看到,channel的参数如果是AioSocketChannel,则创建异步的Channel


看完了client端使用Bootstrap,我们看看ServerBootstrap
server端,多了一个InetSocketAddress 具体的IP地址。同时,有一个接受处理器:
private final ChannelHandler acceptor = new ChannelInitializer<Channel>(){
    @Override
    public void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(new Acceptor());
    }
};


与此同时,服务端还多了一组 ChannelOption和AttributeKey,还有相应的EventLoopGroup以及ChannelHandler,这些前面都有“child”
看函数 public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)
我们就明白了,其实这里的child就是client端的,而parent就是作为acceptor。

在channel(Class<? extends Channel> channelClass)函数中,多了一个对于channelClass是否是ServerChannel的子类。然后是channelClass是否是异步Channel,如果是则设置AioServerSocketChannelaFactory

ServerBootstrap中还可以设置child的参数选项和属性。
对于ServerBootstrap, 比较重要的是Acceptor,这个类其实处理了连接过来的Channel
rivate class Acceptor
            extends ChannelInboundHandlerAdapter implements ChannelInboundMessageHandler<Channel> {

        @Override
        public MessageBuf<Channel> newInboundBuffer(ChannelHandlerContext ctx) throws Exception {
            return Unpooled.messageBuffer();
        }

        @Override
        public void freeInboundBuffer(ChannelHandlerContext ctx, ChannelBuf buf) throws Exception {
            // Nothing to free
        }

        @SuppressWarnings("unchecked")
        @Override
        public void inboundBufferUpdated(ChannelHandlerContext ctx) {
            MessageBuf<Channel> in = ctx.inboundMessageBuffer();
            for (;;) {
                Channel child = in.poll();
                if (child == null) {
                    break;
                }

                child.pipeline().addLast(childHandler);

                for (Entry<ChannelOption<?>, Object> e: childOptions.entrySet()) {
                    try {
                        if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
                            logger.warn("Unknown channel option: " + e);
                        }
                    } catch (Throwable t) {
                        logger.warn("Failed to set a channel option: " + child, t);
                    }
                }

                for (Entry<AttributeKey<?>, Object> e: childAttrs.entrySet()) {
                    child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
                }

                try {
                    childGroup.register(child);
                } catch (Throwable t) {
                    child.unsafe().closeForcibly();
                    logger.warn("Failed to register an accepted channel: " + child, t);
                }
            }
        }
    }



很容易理解了就是得到客户端Channel后使用child的变量设置一番。

至此,我们就大概了解Bootstrap的功能了。同时也看到一点如何使用netty的影子了。
之后可以顺藤摸瓜,一窥netty的各种实现。









分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics