Channel的講解
- 閱讀前需思考的問題
- Netty中的Channel是什么?
- Channel是怎么創(chuàng)建的?
- Channel的創(chuàng)建時機?
- Netty中的Channel和JDK中的Channel什么關系?
- 兩者什么時候關聯(lián)起來的,怎么關聯(lián)的?
Channel是什么?
Channel是對socket連接的封裝,可以理解成Channel就是socket連接;Netty中的Channel分為NioSocketChannel(客戶端)和NioServerSocketChannel(服務端)兩種。 我們知道Netty是基于Java Nio的,那么與Java中的SocketChannel什么關系呢?是一對一的關系。
NioSocketChannel ---------> SocketChannel (1:1)
NioServerSocketChannel ---------> ServerSocketChannel (1:1)
Channel如何實例化的?
上圖是Netty官網的Echo Server示例,左邊是client端代碼,右邊是server端代碼。NioSocketChannel和和NioServerSocketChannel都賦值給channel(...)方法了。
點進ServerBootstrap的channel方法一看究竟:
## AbstractBootstrap類
public B channel(Class<? extends C> channelClass) {
// 以channelClass為參數(shù),創(chuàng)建了ReflectiveChannelFactory對象
// 從名字上看,是通過反射技術創(chuàng)建channel實例的工廠
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
繼續(xù)點進去:
## ReflectiveChannelFactory類
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
// 構造函數(shù)成員變量:constructor
private final Constructor<? extends T> constructor;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
// 獲取一個無參構造函數(shù)
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
@Override
public T newChannel() {
try {
// 通過無參構造函數(shù),創(chuàng)建一個實例
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
......
}
到此我們知道,在 newChannel() 方法中利用 反射技術 創(chuàng)建Channel實例的,此類使用了我們非常熟悉的工廠設計模式。
思考:我們知道ReflectiveChannelFactory是利用反射技術創(chuàng)建對象的,是不是可以創(chuàng)建任意對象?不是的,泛型T是有約束條件的,T 必須是實現(xiàn)Channel接口的類才行的。
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T>
## 泛型T是有約束條件的,T 必須是實現(xiàn)Channel接口的類
Channel無參構造函數(shù)做了什么?
來看下NioServerSocketChannel類:
## NioServerSocketChannel類
public NioServerSocketChannel() {
// 創(chuàng)建一個Socket連接,傳遞給了一個參數(shù)的構造函數(shù)
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
// 創(chuàng)建了一個服務端Socket連接
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
// 使用JDK的SelectorProvider,開啟了一個SocketChannel
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
public NioServerSocketChannel(ServerSocketChannel channel) {
// 創(chuàng)建好的SocketChannel傳遞父類構造函數(shù),同時Accpet事件也傳遞給了父類,后面會用到
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
到此可知,Netty的Channel中維護著一個JDK的SocketChannel,所以Netty的Channel和JDK的Channel關系是1:1。
我們知道Nio是非阻塞的,來看看什么時候配置?
一直點super(...)下去,直到:
## AbstractNioChannel類
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
// 客戶端監(jiān)聽OP_READ事件,服務端監(jiān)聽OP_ACCEPT事件,后面會用到
this.readInterestOp = readInterestOp;
try {
// 客戶端和服務端的SocketChannel都需要配置非阻塞
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);
}
}
Channel何時被創(chuàng)建的?
- 對于NioSocketChannel來說,它是在Bootstrap的connect(...)時被創(chuàng)建的。
- 對于NioServerSocketChannel來說,它是在ServerBootstrap的bind(...)時被創(chuàng)建的。
以服務端的NioServerSocketChannel來研究何時被創(chuàng)建的,客戶端NioSocketChannel創(chuàng)建過程差不多。
來看ServerBootstrap的bind(...)方法源碼:
## AbstractBootstrap類
public ChannelFuture bind(int inetPort) {
// 服務端SocketChannel綁定監(jiān)聽端口,格式:0.0.0.0:Port
return bind(new InetSocketAddress(inetPort));
}
一直點下去,會跟到doBind(...)方法:
## AbstractBootstrap類
private ChannelFuture doBind(final SocketAddress localAddress) {
// 對channel進行初始化和注冊操作
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
......
}
來看下initAndRegister(...)方法:
## AbstractBootstrap類
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 創(chuàng)建一個NioServerSocketChannel實例
channel = channelFactory.newChannel();
// 初始化channel配置
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);
}
// 服務端channel注冊到bossGroup線程池,由bossGroup線程池來處理accept事件
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
到此,我們知道了服務端的Channel是在AbstractBootstrap類的initAndRegister()方法中創(chuàng)建的。