文章目录
- Bootstrap和ServerBootstrap
- 一、AbstractBootstrap
- 1.1 doBind和doBind0
- 1.2 initAndRegister
- 二、Bootstrap
- 2.1 connect
- 2.2 doResolveAndConnect和doResolveAndConnect0
- 2.3 init
- 2.4 doConnect
- 三、ServerBootstrap
- 3.1 属性和构造方法
- 3.2 bind方法
- 3.3 init方法
- 3.4 ServerBootstrapAcceptor
- 3.4.1 channelRead
- 3.4.2 exceptionCaught
- 四、小结
- 五、参考
Bootstrap和ServerBootstrap
- Bootstrap和ServerBootstrap分别是Netty客户端和服务端的启动器,编写Netty应用时也是从这两个类开始的,具体可以参考 :Netty启动分析和组件设计
- 继承关系如下:
一、AbstractBootstrap
- AbstractBootstrap是一个抽象类,Bootstrap和ServerBootstrap的都继承自AbstractBootstrap抽象类。AbstractBootstrap提供了类似于建造者模式的相关方法,我们可以通过方法链来添加配置参数,下面是带注释源码,篇幅原因部分源码已经省略,包含属性和属性设置相关部分;
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
volatile EventLoopGroup group;
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory;
private volatile SocketAddress localAddress;
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
private volatile ChannelHandler handler;
AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
group = bootstrap.group;
channelFactory = bootstrap.channelFactory;
handler = bootstrap.handler;
localAddress = bootstrap.localAddress;
synchronized (bootstrap.options) {
options.putAll(bootstrap.options);
}
synchronized (bootstrap.attrs) {
attrs.putAll(bootstrap.attrs);
}
}
public B group(EventLoopGroup group) {
if (group == null) {
throw new NullPointerException("group");
}
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return self();
}
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
@SuppressWarnings({"unchecked", "deprecation"})
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
public B localAddress(SocketAddress localAddress) {
this.localAddress = localAddress;
return self();
}
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return self();
}
@Override
@SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
public abstract B clone();
public ChannelFuture register() {
validate();
return initAndRegister();
}
public ChannelFuture bind() {
validate();
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
throw new IllegalStateException("localAddress not set");
}
return doBind(localAddress);
}
abstract void init(Channel channel) throws Exception;
public B handler(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
return self();
}
public abstract AbstractBootstrapConfig<B, C> config();
static void setChannelOptions(Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {
for (Map.Entry<ChannelOption<?>, Object> e : options.entrySet()) {
setChannelOption(channel, e.getKey(), e.getValue(), logger);
}
}
}
- 这里面大部分方法都是设置相关的参数,对于channel方法需要注意,channel设置通道类型,内部实际上根据传入的类型参数初始化好了ChannelFactory channelFactory这个Channel工厂对象,后续会使用该工厂对象来生产Channel对象。这里面默认是使用ReflectiveChannelFactory这个ChannelFactory的实现类,内部采用反射的方式创建Channel对象,下面给出一点核心代码
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
}
- option方法设置ChannelOption选项,可以对通道的属性和行为进行一定的定制,比较典型的有:通过b.option(ChannelOption.SO_KEEPALIVE, true)来设置连接通道保持连接,这也是一个非常重要的方法。
- handler方法设置事件处理器来处理用户的逻辑,不过一般会设置一个ChannelInitializer的实现类来设置用户的若干个具体ChannelHandler
1.1 doBind和doBind0
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println(Thread.currentThread() + ": PendingRegistrationPromise");
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
private static void doBind0( final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + ": bind");
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
1.2 initAndRegister
- initAndRegister是初始化和注册Channel的主流程,内部会调用init方法
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;
}
二、Bootstrap
- Bootstrap是客户端的启动器,除了继承自AbstractBootstrap之外,Bootstrap本身主要是完成对远程地址的连接,因此内部的属性方法主要是围绕完成连接做的,另外也重写了父类的抽象方法init来完成Channel的初始化。
- 注释代码如下:
public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;
private final BootstrapConfig config = new BootstrapConfig(this);
@SuppressWarnings("unchecked")
private volatile AddressResolverGroup<SocketAddress> resolver = (AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
private volatile SocketAddress remoteAddress;
private Bootstrap(Bootstrap bootstrap) {
super(bootstrap);
resolver = bootstrap.resolver;
remoteAddress = bootstrap.remoteAddress;
}
@SuppressWarnings("unchecked")
public Bootstrap resolver(AddressResolverGroup<?> resolver) {
this.resolver = (AddressResolverGroup<SocketAddress>) (resolver == null ? DEFAULT_RESOLVER : resolver);
return this;
}
public Bootstrap remoteAddress(SocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
return this;
}
public ChannelFuture connect() {
validate();
SocketAddress remoteAddress = this.remoteAddress;
if (remoteAddress == null) {
throw new IllegalStateException("remoteAddress not set");
}
return doResolveAndConnect(remoteAddress, config.localAddress());
}
}
2.1 connect
- connect方法是Bootstrap最重要的一个方法,当构造以及配置好了之后,通过连接方法开始建立连接以及后续的发送和接收信息处理。提供了一系列重载的方法,逻辑完全是一样的,我们这里这分析其中一个方法。
public ChannelFuture connect(SocketAddress remoteAddress) {
validate();
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
return doResolveAndConnect(remoteAddress, config.localAddress());
}
- 我们看到connect校验参数之后,是通过doResolveAndConnect来完成连接过程的,validate逻辑比较简单就不解析了
2.2 doResolveAndConnect和doResolveAndConnect0
- doResolveAndConnect完成地址解析并连接,内部会调用父类的initAndRegister方法,父类的initAndRegister方法里面又会走子类重写的init方法
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.isDone()) {
if (!regFuture.isSuccess()) {
return regFuture;
}
return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
} else {
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();
doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
}
}
});
return promise;
}
}
private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
final SocketAddress localAddress, final ChannelPromise promise) {
try {
final EventLoop eventLoop = channel.eventLoop();
final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
doConnect(remoteAddress, localAddress, promise);
return promise;
}
final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
if (resolveFuture.isDone()) {
final Throwable resolveFailureCause = resolveFuture.cause();
if (resolveFailureCause != null) {
channel.close();
promise.setFailure(resolveFailureCause);
} else {
doConnect(resolveFuture.getNow(), localAddress, promise);
}
return promise;
}
resolveFuture.addListener(new FutureListener<SocketAddress>() {
@Override
public void operationComplete(Future<SocketAddress> future) throws Exception {
if (future.cause() != null) {
channel.close();
promise.setFailure(future.cause());
} else {
doConnect(future.getNow(), localAddress, promise);
}
}
});
} catch (Throwable cause) {
promise.tryFailure(cause);
}
return promise;
}
2.3 init
@Override
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(config.handler());
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()) {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
}
}
2.4 doConnect
private static void doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
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);
}
});
}
三、ServerBootstrap
- ServerBootstrap是服务端的启动器, ServerBootstrap也实现了父类的init方法用于Channel的初始化,关键属性包括childGroup(对应Reactor中的subReactor)、childHandler(childGroup的事件处理器)、其他的比如选项,配置,参数校验之类的和前面2个类差不多,这里就看下关键的地方
3.1 属性和构造方法
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;
private ServerBootstrap(ServerBootstrap bootstrap) {
super(bootstrap);
childGroup = bootstrap.childGroup;
childHandler = bootstrap.childHandler;
synchronized (bootstrap.childOptions) {
childOptions.putAll(bootstrap.childOptions);
}
synchronized (bootstrap.childAttrs) {
childAttrs.putAll(bootstrap.childAttrs);
}
}
- ServerBootstrap有两个EventLoopGroup的属性,一个是继承自AbstractBootstrap的group(parentGroup),另一个是自定义的childGroup,其中parentGroup负责处理ServerChannel(接收客户端连接通道)的I/O事件, 而chidGroup则是用于处理Channel(客户端连接通道)的I/O事件。
3.2 bind方法
- bind方法用于创建Channel并绑定,bind有很多重载方法,我们分析一个
public ChannelFuture bind() {
validate();
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
throw new IllegalStateException("localAddress not set");
}
return doBind(localAddress);
}
- doBind是主逻辑;分析发现doBind和Bootstrap的doResolveAndConnect方法很类似
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println(Thread.currentThread() + ": PendingRegistrationPromise");
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
3.3 init方法
@Override
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());
}
}
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
System.out.println(Thread.currentThread() + ": user handler");
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + ": ServerBootstrapAcceptor");
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
3.4 ServerBootstrapAcceptor
- 内部类ServerBootstrapAcceptor继承ChannelInboundHandlerAdapter,是一个入站处理器,Netty将ServerChannel接受客户端连接的accept事件抽象为Read读取事件。因此重点关注ServerBootstrapAcceptor的channelRead()方法,其完成的工作有:
1.配置Channel,包括Channel上的处理器链,Channel的选项参数及属性键值对。
2.将服务端accept的客户端Channel注册到subReactor线程池的一个线程上
3.4.1 channelRead
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);
}
}
3.4.2 exceptionCaught
- exceptionCaught方法用于处理当ServerChannel事件在执行中产生异常时,此时不会关闭ServerChannel,因为还有其他的客户端连接需要处理。Netty处理异常时的机制是:产生异常后暂停接受客户端连接,1s以后再恢复接受连接。
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final ChannelConfig config = ctx.channel().config();
if (config.isAutoRead()) {
config.setAutoRead(false);
ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
}
ctx.fireExceptionCaught(cause);
}
四、小结
- Bootstrap和ServerBootstrap分表是客户端和服务端的启动器,二者都是继承自AbstractBootstrap
- 在里面的注册绑定逻辑中,大量使用了异步编程模式,这样耗时操作就不会阻塞主线程,然后再通过Future去获取
- 在继承体系里面使用了模板模式和建造者模式,initAndRegister固定了流程,init交给子类实现,建造者模式可以方便编程时设置参数
五、参考
- [1] Netty启动分析和组件设计
- [2] netty源码分析系列——Bootstrap