四、netty的bootstrap

        之前看过不少文章都是从bootstrap讲起来的,这样的好处显而易见,可以较快的对Netty的整体结构,各种功能间的协作关系有一个认知。但是当时自己对selector,channel等都不甚理解,所以看的也是云里雾里。其实这次也是我阅读源码的一个顺序吧,把BootStrap放在了这样一个位置再来学习。

        实际上分析完了基础组件再回头看启动过程,其实会发现这个过程也是蛮简单的,我们先来看AbstractBootStrap, 主要成员变量如下   

volatile EventLoopGroup group;   //线程池
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory;   // channel工厂
private volatile SocketAddress localAddress;  
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();  //channel选项
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();  //channel属性

        那么这里要说明一下的是options和attrs,其中options是关于channel的一些选项,我们可以进入到ChannelOptions里面大致看一下,里面实际上已经给我们定义好了许多的常量,

public static final ChannelOption<Boolean> SO_BROADCAST = valueOf("SO_BROADCAST");
public static final ChannelOption<Boolean> SO_KEEPALIVE = valueOf("SO_KEEPALIVE");
public static final ChannelOption<Integer> SO_SNDBUF = valueOf("SO_SNDBUF");
public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");
public static final ChannelOption<Boolean> SO_REUSEADDR = valueOf("SO_REUSEADDR");
public static final ChannelOption<Integer> SO_LINGER = valueOf("SO_LINGER");
public static final ChannelOption<Integer> SO_BACKLOG = valueOf("SO_BACKLOG");
public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");

        实际上它决定了channel的一些工作方式,比如说SO_KEEPALIVE便是tcp连接的存活时间。

        而attrs是用户自定义的一些属性,可以在挂在在上面随着ctx一起传播。

        AbstractBootStrap最重要的功能是提供了绑定地址的方法,首先在

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        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);
    }

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

        方法中,通过channelFactory new出来一个channel,init(channel)为抽象方法交给子类去实现,接下来将channel注册到eventLoopGroup中去,最后返回一个future,源码注释中也给出了为何要使用future而不是直接返回一个channel,原因是:接下来要进行的bind或者connect的动作,必须要在channel已经注册到eventloop以后才能进行,但是register过程并不一定在同一个线程内进行,所以用future来确保进行下一步动作的时候,register动作已经做完。在接下来的代码中,就能体现出future的作用:        

// 如果注册动作已经完成,则执行绑定
if (regFuture.isDone()) {
    ChannelPromise promise = channel.newPromise();
    doBind0(regFuture, channel, localAddress, promise);
    return promise;
} else {
//否则,监听future绑定动作的完成
    final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
    regFuture.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            Throwable cause = future.cause();
            if (cause != null) {
                promise.setFailure(cause);
            } else {
                promise.registered();

                doBind0(regFuture, channel, localAddress, promise);
            }
        }
    });
    return promise;
}

        接下来我们来看服务端启动器ServerBootstrap的实现,ServerBootstrap比较的一个特色就是它支持传入两个线程池BossGroup和WorkerGroup,Bossgroup只负责处理connect事件,当新的连接建立好之后,就交给WorkerGroup来处理具体的读写等事件。那么这个过程是怎么实现的,实际上当NioSocket接收到accept事件时,它会调用对应channel的read()方法,首先执行AbstractNioMessageChannel.Unsafe的read()方法,随后执行到子类NioServerSocketChannel的doReadMessages(List<Object> buf)的方法,这里获取到的是一个channel,随后通过fireChannelRead传播事件。而在ServerBootstrap中,它自己实现了一个handler。

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;

    child.pipeline().addLast(childHandler);

    setChannelOptions(child, childOptions, logger);

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

    try {
        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);
    }
}

其中的read方法将读到的channel注册到child上,即注册到了workerGroup上,所以bossGroup上注册的channel只有服务端channel一个,所以它只要处理此channel上发生的事件就可以了,也就是注册事件。而每一个服务端与客户端之间的连接channel都在workerGroup上注册,所以workerGroup要处理的事情就要多得多了,最后上张图。

大概就是这样子,反正我是懂了。

        那么刚才提到AbstractBossgroup会调用子类的init()方法,那么ServerBootstrap中init做的处理也比较简单了,主要就是把刚才我们讲到的handler添加到channel的pipeline上,这里的channel就是tcp服务端绑定的那个端口了。

        服务端的启动器Bootstrap相对而言,就更简单了,主要功能便是connect,而connect的过程和Server端一样。首先通过initAndRegister()初始化channel并把它注册到eventLoopGroup上,随后在init中设置channel的options,加上attrs,在pipeline上添加handlers。最后调用对应的unsafe的connect()方法。

        

转载于:https://my.oschina.net/vqishiyu/blog/2995453

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值