1 如何在抽象类中方法返回实体类?
类似于构造器的思路,调用方法后返回本身;
抽象类中通过泛型,引入了继承它本身的类,这种做法,可以帮助抽象类返回实现类;
2 使用ChannelFactory工厂的好处?
AbstractBootstrap {
private volatile ChannelFactory<? extends C> channelFactory;
这里提供了两种创建channel的思路:
-
第一种直接设置channel
这种方法使用默认的BootstrapChannelFactory构造,存在channel灵活性问题,如果是自己定义的channel则不会有很好的扩展性.
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new BootstrapChannelFactory<C>(channelClass));
}
- 第二种设置工厂类的方式
这种方法的优势是创建何种channel可以由外部扩展决定!目前看到的ovsdb等南向协议使用第一种直接设置NioServerSocketChannel
或者NioSocketChannel
居多
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
3 有哪些options和attrs?具体含义?可否扩展?
3.1 options
- options的一些具体值,如下所示,后续再关注选项在实现channel时候带来的变化!
如下所示为ovsdb中的一些选项设置:
//设置tcp无延迟
bootstrap.option(ChannelOption.TCP_NODELAY, true);
//设置缓冲buf
bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(65535, 65535, 65535));
//设置后台日志???
.option(ChannelOption.SO_BACKLOG, 100)
3.2 attr
attr目前用的并不多,个人认为这是用户基于channel做的一些定制属性,便于用户扩展和识别channel留下来的!
4 options和attrs如何与channel关联?
在Bootstrap和ServerBootstrap中都有一段类似的代码,bootstrap的初始化接口init,会把ChannelPipline\options\attr属性均初始化给Channel
5 Channel和handler如何关联?
上图init函数的第一行实际上已经给出了两者的关联关系, channel中包含了ChannelPipeline,而handler作为pipline里面的众多处理handler一起传给了p.addLast(handler());
,但是具体的服务端和客户端的初始化是不同的,配置handler也有所不同;
下面以ovsdb代码为例展开说明:
5.1 bootstrap如何关联handler?
客户端方式设置的是一个ChannelInitializer
的handler,然后在到里面添加了新的其他的handler!
5.2 ServerBootstrap如何关联handler?
我们可以看到服务端设置两个handler,这里可以这里可以这样理解:
-
服务端主handler以打印日志为主:
-
子handler用于每个连接通道的处理,
不是立马创建而是响应连接添加
这里的ServerBootstrapAcceptor就是一个channelHandler,当接收会话请求后,channelRead的工作就是添加后续需要的handler
—这里有一个值得学习的地方,在运行过程中动态pipline中加入childhandler
6 Bootstrap连接最后一步
bootstrap的连接最后一步就是连接指定服务器或者开启端口接听连接,这里最主要的工作就是初始化channel以及发起连接或监听工作
;
6.1 client Bootstrap
ChannelFuture future = bootstrap.connect(address, port).sync();
Channel channel = future.channel();
这里最重要的是两个处理,初始化channel,这里涉及到4-5节内容,不具体展开,以及最重要的doResolveAndConnect0--见第七节
6.2 server ServerBootstrap
这里是serverBootstrap的最后一步:
ChannelFuture channelFuture = serverBootstrap.bind(ip, port).sync();
ServerBootstrap的主要功能在doBind完成,这里最重要的操作也是两个:
- initAndRegister-- 初始化channel,见4-5节内容;
- doBind0 – 具体绑定事宜处理,见第8节内容;
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
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) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
最重要的方法是initAndRegister
7 客户端doResolveAndConnect0做了什么?
有了Channel,剩下的工作就是连接了,这里暂时把ChannelPromise
的处理留意下,准备后续在研究,重点看看doConnect
;
private static void doConnect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
final Channel channel = connectPromise.channel();
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (localAddress == null) {
channel.connect(remoteAddress, connectPromise);
} else {
channel.connect(remoteAddress, localAddress, connectPromise);
}
connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
});
}
这里把connect的调用部分调用列出来,至于channel的处理逻辑,后续在专题讨论!
8 服务端 doBind0做了什么?
如下所示为doBind0的代码:
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
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是在客户端连接请求到来后,开启channel的绑定工作.
这里也先给出调用逻辑,后续再做详细分析
9 遗留问题
1 ChannelPromise在doResolveAndConnect0的作用?
2 channel的connect和bind做了什么后续分析?