在普通IO
中,通过把机器传输抽象成java.net.Socket
流,也就是Socket
和ServerSocket
到NIO
时,则变为了java.nio.channels.Channel
,也是作为传输的管道,具体可看Java Netty 学习(四) - NIO基础知识Channel和Pipe
那今天就一起学习Netty中的Channel,看它又是一种怎么样的设计形式。
介绍
在netty的Channel中,提供了很多共用的API,相比与大大降低了直接使用Socket
的复杂性。另外,它是Netty网络通信的主题,由他负责直接对端进行网络通信,注册,以及相关的数据操作。
Channel提供了许多预定义的专门实现的类:
- EmbeddedChannel:用于测试ChannelHandler的类
- LocalServerChannel:同一个JVM内部实现client和server之间通信的Channel
- NioDatagramChannel:使用UDP进行网络传输,和java.net.DatagramSocket对应
- NioSctpChannel:基于Sctp协议的操作
- NioSocketChannel:使用TCP进行网络传输
结构
Channel接口里面主要有以下方法:
- ChannelId id():返回唯一的channel id
- EventLoop eventLoop():返回专属被注册进入的EventLoop
- Channel parent():返回父channel
- ChannelConfig config():返回channel的config
- SocketAddress localAddress():返回自己的socketAddress
- SocketAddress remoteAddress():返回远端的Address
- Unsafe unsafe():返回unsafe,内部实现的unsafe,一些方法只能用于I/O线程调用,其他的如localAddress则可以被用户线程调用。
状态
按照Channel定义的模型,Channel有四个状态
- ChannelUnregistered:Channel已经被创建,但还未注册到EventLoop
- ChannelRegistered:Channel已经被注册到EventLoop
- ChannelActive:Channel处于活动状态,他现在可以接受和发送数据了
- ChannelInactive:Channel没有连接到远程节点
NioSocketChannel
下面看看NioSocketChannel相关特性:
赋值
记得上一篇文章给出的实例,Bootstrap调用的建造者模式的过程中用到了NioSocketChannel
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HelloClientHandler());
}
});
下面跟着源码看看里面做了些什么事:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
再看channelFactory:
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
最后设置以下channelFactory:
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
在channel方法里面,首先还是返回一个AbstractBootstrap
的子类以供继续建造对象,接着把一个
ReflectiveChannelFactory
对象传递给了channelFactory
,并且以传入的channelClass为参数,名字上可知是返回一个channel类,包装了很多层工厂方法为了实现代码的统一性。
进入源码可知,ReflectiveChannelFactory
是一个继承了ChannelFactory
的对象里面有一个newChannel
方法如下:
@Override
public T newChannel() {
try {
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
执行了传入参数的Class的newInstance
方法。
现在已经清楚知道Bootstrap中的channel方法做了什么事了,就是把一个相应类型的ChannelFactory传进去,这样就可以产生Channel。
那么,NioSocketChannel什么时候被初始化的呢?
初始化
首先在NioSocketChannel中打断点,跟踪到调用服务:
可以很清楚的看到
由connect->doResolveAndConnect->initAndRegister->newChannel一级一级调用:
返回一个异步的ChannelFuture
ChannelFuture future = b.connect(this.address, this.port);
再到:
public ChannelFuture connect(String inetHost, int inetPort) {
return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
}
接着在doResolveAndConnect
执行初始化一个Channel,但此时并没有连接上,就相当先拿到一个默认配置的Channel,但是现在还没有用。
接着执行initAndRegister
,并在这里面调用:
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);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
从而再经过JNI等一步步反射调用NioSocketChannel的构造方法。
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
用途
那么,Channel创建了,也初始化了,那么它在Netty里面,地位是啥呢?
先看看一张图:
上图有以下几个概念:
- 客户端连接成功,就新建一个Channel来接受(代码中是先初始化一个Channel,连接成功后赋值属性)
- EventLoopGroup分配一个EventLoop给Channel,并注册到该EventLoop,channel生命周期内都和该EventLoop在一起(不变)同时,注册时获得selectionKey
- Channel进行网络连接、关闭和读写,生成相对应的event(改变selectinKey信息),触发eventloop调度线程进行执行
- EventLoop执行调度ChannelPipline来处理用户业务逻辑
并且,多个Channel可以同时注册到同一个EventLoop,并按照顺序循环处理Channel中的逻辑。
总结
Channel就类似于一个容器,或者理解为Socket,通过它与另一端进行通信,由EventLoop来处理Channel中产生的事件,在事件中实现相关逻辑,接下来文章将逐步学习其他组件。
帮助: