1.Bootstrap
Bootstrap是Netty框架引导客户端的入口, 引导过程中需要开发人员对其进行一些配置
- EventLoopGroup
- Channel
- ChannelOption
- ChannelAttr
- ChannelHandler
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(new NioEventLoopGroup());
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.remoteAddress("127.0.0.1", 30888);
final MessageChannel channel = new MessageChannel();
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//配置Pipeline
}
});
ChannelFuture channelFuture = bootstrap.connect().sync();
channelFuture.channel().closeFuture().sync();
bootstrap.group().shutdownGracefully();
使用了Nio的网络模型, 使用了30888端口号, 并使用ChannelInitializer类型的ChannleHandler作为ChannelHandler的初始化器。
ChannelInitializer的作用是当客户端连接成功后初始化ChannelPipeline
Bootstrap是继承自AbstractBootstrap类
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
@SuppressWarnings("unchecked")
private static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
@SuppressWarnings("unchecked")
private static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
volatile EventLoopGroup group;
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory;
private volatile SocketAddress localAddress;
// The order in which ChannelOptions are applied is important they may depend on each other for validation
// purposes.
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
private volatile ChannelHandler handler;
//构造函数与定义的方法
}
定义在AbstractBootstrap抽象类中的方法应该是Bootstrap与ServerBootstrap都能使用到的方法
2.客户端启动流程
2.1 connect方法
Bootstap.connect()
public ChannelFuture connect(SocketAddress remoteAddress) {
ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
validate();
return doResolveAndConnect(remoteAddress, config.localAddress());
}
validate() 检验参数的触发器是否存在, 如果不存在抛出异常
Bootstrap.doResolveAndConnect()
AbstractBootstrap.initAndRegister()
-
使用channelFactory.newChannel()创建channel对象,channelFactory是在bootstrap.channel()方法中初始创建
-
调用子类的init()方法初始channel对象,这一过程会调用bootstrap.handler()方法,将配置的channelHandler添加到channel的pipeline中。
-
配置channelOption与attributes到channel对象
-
将初始完成的Channel对象注册到EventLoopGroup中
- 通过channelFactory反射创建NioSocketChannel
- 设置一些初始化参数
- 将channel注册如EventLoopGroup
SingleThreadEventLoop.register()
AbstractUnsafe.register()
- 保持eventLoop
- register0()实现: 实现注册selector
2.2 doResolveAndConnect0
-
使用EventLoop创建一个地址解析器,异常时将结束方法调用
-
判断当前地址是否已经解析过,解析过的地址就直接调用doConnect()方法
-
没有解析过的地址先进行解析,再用解析后的地址调用doConnect()方法
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)) {
// Resolver has no idea about what to do with the specified remote address or it's resolved already.
doConnect(remoteAddress, localAddress, promise);
return promise;
}
final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
if (resolveFuture.isDone()) {
final Throwable resolveFailureCause = resolveFuture.cause();
if (resolveFailureCause != null) {
// Failed to resolve immediately
channel.close();
promise.setFailure(resolveFailureCause);
} else {
// Succeeded to resolve immediately; cached? (or did a blocking lookup)
doConnect(resolveFuture.getNow(), localAddress, promise);
}
return promise;
}
// Wait until the name resolution is finished.
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;
}
最后将由我们在Bootstrap类型中赋值时的NioSocketChannel.doConnect()执行真正的连接操作, 而这一过程又是由ChannelPipeline的connect事件完成
所以最后NioSocketChannel.doConnect()方法的责任则是调用java.nio.channels.SocketChannel.connect()完成。
3.AbstractBootstrap抽象方法
- group 配置EventLoopGroup,为服务端时将指定为parent,时间主子Reactor模型
- self 返回当前类型,也就是自己
- channel 配置Channel,并初始化channelFactory
- channelFactory 和配置Channel相同,通过配置这一项可以覆盖channel的配置
- localAddress 配置本地地址
- option 配置channel的选项
- attr 配置Channel的属性
AbstractBootstrap中常用的能用方法,这些方法在服务端也会用到。
4.总结
Nio模式下客户端的引导通过在Bootstrap上配置的NioSocketChannel、NioEventLoopGroup、ChannelInitializer等预先配置信息,在调用Bootstrap.connect()方法后,在其内部通过channelFactory创建channel,调用initAndRegister()配置option与attr并注册到eventLoopGroup中,再通过解析出的远程地址连接远程服务,连接的过程是通过channel.pipeline中的事件传递完成,最后由配置在Bootstarp中的channel(也就是NioSocketChannel)实际做出连接的动作。是否连接成功可以通过Bootstrap.connect()返回的ChannelFuture类型实例进行判断。