详解Netty组件: Channel

767 阅读4分钟

1 AbstractChannel

1.1 AbstractChannel构造

通道是Netty的核心概念之一,代表网络连接,由它负责同对端进行网络通信,既可以写入数据到对端,也可以从对端读取数据。

Netty通道的抽象类AbstractChannel的构造函数如下:

private final DefaultChannelPipeline pipeline;
private final Channel parent;

protected AbstractChannel(Channel parent) {
    this.parent = parent; // 父通道
    id = newId();
    // 新建一个底层的NIO 通道,完成实际的IO操作
    unsafe = newUnsafe();
    //新建一条通道流水线
    pipeline = newChannelPipeline();
}
  • newUnsafe方法是具体的子类中实现,是个抽象方法

  • AbstractChannel内部有一个pipeline属性,表示处理器的流水线。Netty在对通道进行初始化的时候,将pipeline属性初始化为DefaultChannelPipeline的实例。表明每个通道都拥有一条ChannelPipeline处理器流水线。

AbstractChannel内部有一个parent父通道属性,保持通道的父通道。

  • 对于连接监听通道(如NioServerSocketChannel)来说,其parent属性的值为null;
   public NioServerSocketChannel(ServerSocketChannel channel) {
       // parent穿了一个null
     super(null, channel, SelectionKey.OP_ACCEPT);
     config = new NioServerSocketChannelConfig(this, javaChannel().socket());
 }
  • 对于传输通道(如NioSocketChannel)来说,其parent属性的值为接收到该连接的监听通道。

几乎所有的Netty通道实现类都继承了AbstractChannel抽象类,都拥有上面的parent和pipeline两个属性成员。

1.2 AbstractChannel主要方法

// 此方法的作用为连接远程服务器。
// 方法的参数为远程服务器的地址,调用后会立即返回
// 其返回值为执行连接操作的异步任务ChannelFuture。
// 此方法在客户端的传输通道使用。
ChannelFuture connect(SocketAddress address)

// 此方法的作用为绑定监听地址,开始监听新的客户端连接。
// 此方法在服务器的新连接监听和接收通道时调用。
ChannelFuture bind(SocketAddress address)

// 此方法的作用为关闭通道连接,返回连接关闭的ChannelFuture异步任务。
// 如果需要在连接正式关闭后执行其他操作,则需要为异步任务设置回调方法;
// 或者调用ChannelFuture异步任务的sync()方法来阻塞当前线程,一直等到通道关闭的异步任务执行完毕。
ChannelFuture close();

// 此方法的作用为读取通道数据,并且启动入站处理。
// 具体来说,从内部的Java NIO Channel通道读取数据,
// 然后启动内部的Pipeline流水线,开启数据读取的入站处理。
// 此方法的返回通道自身用于链式调用。
Channel read()

// 此方法的作用为启程出站流水处理,
// 把处理后的最终数据写到底层通道(如Java NIO通道)。
// 此方法的返回值为出站处理的异步处理任务。
ChannelFuture write(Object o)

// 此方法的作用为将缓冲区中的数据立即写出到对端。
// 调用前面的write()出站处理时,并不能将数据直接写出到对端,
// write操作的作用在大部分情况下仅仅是写入操作系统的缓冲区,
// 操作系统会根据缓冲区的情况决定什么时候把数据写到对端。
// 执行flush()方法会立即将缓冲区的数据写到对端。
Channel flush()

2 EmbeddedChannel:嵌入式通道

。一般单元测试的大致流程是:先将Handler业务处理器加入到通道的Pipeline流水线中,接下来先后启动Netty服务器、客户端程序,相互发送消息,测试业务处理器的效果。

这些复杂的工序存在一个问题:如果每开发一个业务处理器都进行服务器和客户端的重复启动,那么整个的过程是非常烦琐和浪费时间的。如何解决这种徒劳、低效的重复工作呢?Netty提供了一个专用通道,即EmbeddedChannel(嵌入式通道)。

EmbeddedChannel仅仅是模拟入站与出站的操作,底层不进行实际传输,不需要启动Netty服务器和客户端。除了不进行传输之外,EmbeddedChannel的其他事件机制和处理流程和真正的传输通道是一模一样的。因此,使用EmbeddedChannel,开发人员可以在单元测试用例中方便、快速地进行ChannelHandler业务处理器的单元测试。

为了模拟数据的发送和接收,EmbeddedChannel提供了一组专门的方法,

// 向通道写入入站数据,模拟真实的收到数据场景
// 这些数据会被流水线上的入站处理器处理
public boolean writeInbound(Object... msgs)

// 从EmbeddedChannel读读取入站数据,返回经过流水线最后
// 一个入站处理器处理完成之后的入站数据
// 如果没有数据返回null
public <T> T readInbound()

// 向通道写入出站数据,模拟真实的通道发送数据场景
// 会被流水线上的出站处理器处理
public boolean writeOutbound(Object... msgs)

// 从EmbeddedChannel读读取出站数据,返回经过流水线最后
// 一个出站处理器处理完成之后的出站数据
// 如果没有数据返回null
public <T> T readOutbound() 

// 结束EmbeddedChannel,调用channel的close方法
public boolean finish()

EmbeddedChannel类既拥有通道的通用接口和方法,又增加了一些单元测试的辅助方法,在开发时是非常有用的