2.Netty源码分析二(Channel)
从Bootstrap入手,在服务端和客户端启动过程中都需要使用到channel()方法,以下是channel()方法源码
//AbstractBootstrap
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
可以看到,以上代码使用了channelFactory()方法,并传入了ReflectiveChannelFactory的实例
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Constructor<? extends T> constructor;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
//该方法是该类中唯一的方法,使用反射直接获取到channel的实例
@Override
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(ReflectiveChannelFactory.class) +
'(' + StringUtil.simpleClassName(constructor.getDeclaringClass()) + ".class)";
}
}
对于io.netty.channel.ReflectiveChannelFactory#newChannel方法,使用了工厂模式➕反射机制,通过无参构造函数实例化channel
- 对于 NioSocketChannel,由于它充当客户端的功能,它的创建时机在
connect(…)
的时候; - 对于 NioServerSocketChannel 来说,它充当服务端功能,它的创建时机在绑定端口
bind(…)
的时候。
以Bootstrap的connect()为入口,观察源码的调用链路
public ChannelFuture connect(InetAddress inetHost, int inetPort) {
return connect(new InetSocketAddress(inetHost, inetPort));
}
public ChannelFuture connect(SocketAddress remoteAddress) {
//非空校验
ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
//校验channelFactory是否存在,以及其他参数是否正确配置
validate();
return doResolveAndConnect(remoteAddress, config.localAddress());
}
继续阅读doResolveAndConnect(..)方法
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
....
}
AbstractBootstrap中的initAndRegister方法就会帮助我们初始化channel,当然,其中的init()方法由对应子类(Bootstrap和ServerBootstrap)分别实现。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
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;
}
根据之前EchoClient可以知道,此时的channel应该是NioSocketChannel,所以此时就需要看下NioSocketChannel的构造方法
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
public NioSocketChannel(SelectorProvider provider) {
this(provider, null);
}
//该构造方法是最终执行的构造方法,可以看到调用了newChannel方法
public NioSocketChannel(SelectorProvider provider, InternetProtocolFamily family) {
this(newChannel(provider, family));
}
newChannel(..)方法,最终生产出的SocketChannel就是JDK NIO 的SocketChannel实例
private static SocketChannel newChannel(SelectorProvider provider, InternetProtocolFamily family) {
try {
SocketChannel channel = SelectorProviderUtil.newChannel(OPEN_SOCKET_CHANNEL_WITH_FAMILY, provider, family);
return channel == null ? provider.openSocketChannel() : channel;
} catch (IOException e) {
throw new ChannelException("Failed to open a socket.", e);
}
}
ServerBootstrap同理,最终会生产JDK NIO ServerScoketChannel实例。
可以继续看下NioSocketChannel的构造方法
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
第一行super调用父类构造器
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
//客户端只关注OP_READ事件,等待读取服务端返回数据
super(parent, ch, SelectionKey.OP_READ);
}
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
//保存SelectionKey.OP_READ
this.readInterestOp = readInterestOp;
try {
//设置channel为非阻塞模式
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
NioServerSocketChannel类似,与客户端区别在于服务端关注SelectionKey.OP_ACCEPT事件
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
对于Netty的channel来说,主要目的就是实例华JDK的channel实例,然后设置非阻塞模式