netty 源码之Bootstrap

Bootstrap 以及 ServerBootstrap类都继承自 AbstractBootstrap
这个抽象类很有趣,请看

public abstract class AbstractBootstrap<B extends AbstractBootstrap<?>> {
...
}

泛型中的类是自身的子类。其实如果明白这个类的作用,就知道为什么要这么写了。
见doc:
[b]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[/b]
也就是说,这是一个帮助类,为了更方便启用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的各种实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值