Bootstrap
主要包含两个部分,一个是服务器地址的解析器组AddressResolverGroup
,另外一个是用来工做的EventLoopGroup
。 EventLoopGroup
负责出人EventLoop
,AddressResolverGroup
负责给EventLoop
解析服务器地址。java
链接远程服务器,会先check引导类(Bootstrap)的group有没有设置以及生成channel的工厂,以后再调用doResolveAndConnect方法。git
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
// 初始化并注册一个channel,并将chanelFuture返回
final ChannelFuture regFuture = initAndRegister();
// 获得实际的channel(初始化和注册的动做可能还没有完成)
final Channel channel = regFuture.channel();
// 当到这chanel相关处理已经完成时
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 {
// Directly obtain the cause and do a null check so we only need one volatile read in case of a
// failure.
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
// 修改注册状态为成功(当注册成功时不在使用全局的executor,使用channel本身的,详见 https://github.com/netty/netty/issues/2586)
promise.registered();
// 进行相关的绑定操做
doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
}
}
});
return promise;
}
}
复制代码
和ServerBootstrap
同样,Bootstrap
也要先去初始化和注册channel,注册的方法和ServerBootstrap
相同,初始化channel的方法有所区别。github
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(config.handler());
// 获取channel的可选项Map
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
// 获取channel的属性Map
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e : attrs.entrySet()) {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
}
}
复制代码
客户端channel的初始化和服务端相比要简单不少,只须要设置一些相关信息便可。promise
channel处理好以后,客户端就会去链接服务器。bash
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
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
channel.close();
promise.setFailure(future.cause());
} else {
// 解析成功开始链接
doConnect(future.getNow(), localAddress, promise);
}
}
});
} catch (Throwable cause) {
promise.tryFailure(cause);
}
return promise;
}
复制代码
当服务器地址是IP的时候,直接链接,若是是域名之类的,会先解析出服务器的IP地址,而后再进行链接。域名解析直接使用的java.net.InetAddress的getByName方法,而链接的方法调用的是sun.nio.ch.SocketChannelImpl的connect方法。服务器
文中帖的代码注释全在:github.com/KAMIJYOUDOU… , 有兴趣的童鞋能够关注一下。ide
本篇到此结束,若是读完以为有收获的话,欢迎点赞、关注、加公众号【贰级天災】,查阅更多精彩历史!!! oop