Netty核心组件-Netty笔记(一)

/ 4

Channel

ChannelJava NIO 的一个基本构造,它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或一个能够执行一个或多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作。

目前,可以把 Channel 看作是传入(入站)或者传出(出站)数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接。

回调

Netty 在内部使用了回调来处理事件,当一个回调被触发时,相关的事件可以被一个 ChannelHandler 接口的实现处理。

当一个新的连接被建立时, ChannelHandlerchannelActive() 回调方法将会被调用。例如:

public class ConnectHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Client " + ctx.channel().remoteAddress() + " connected"); 
    }
}

Future

Future 提供了另一种在操作完成时通知应用程序的方式,这个对象可以看作是一个异步操作的结果的占位符。它将在未来的某个时刻完成,并提供对其结果的访问。

JDK 预置了 java.util.concurrent.Future 接口,但是其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。这是非常频繁的,所以 Netty 提供了它自己的实现 ChannelFuture ,用于在执行异步操作的时候使用。

ChannelFuture 提供了几种额外的方法,这些方法使得我们能够注册一个或多个 ChannelFutureListener 实例。监听器的回调方法 operationComplete() 将会在对应的操作完成时被调用。然后监听器可以判断该操作是否出错,如果是出错了,我们可以检索产生的 Throwable 。简而言之,由 ChannelFutureListener 提供的通知机制消除了手动检查对应的操作是否完成的必要性

每个 Netty 的出站 I/O 操作都将返回一个 ChannelFuture ,也就是说,它们都不会阻塞,所以说 Netty 是异步(非阻塞)和事件驱动的。异步地建立连接:

Channel channel = ...;
ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));

注册一个 ChannelFutureListener 到 connect() 方法返回的 ChannelFuture 上。当监听器被通知连接已经建立(回调 operationComplete() 方法)的时候,要检查对应的状态。如果成功,则将数据写到该 Channel ,否则检索 Throwable 。

future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) {
        if (future.isSuccess()) {
            ByteBuf buffer = Unpooled.copiedBuffer("Hello", Charset.defaultCharset());
            ChannelFuture wf = future.channel().writeAndFlush(buffer);
            // ...
        } else {
            Throwable cause = future.cause();
            cause.printStackTrace();
        }
    }
});

如果 ChannelFutureListener 添加到 ChannelFuture 的时候, ChannelFuture 已经完成,那么该 ChannelFutureListener 将会被直接地通知。

需要注意的是,对错误的处理完全取决于你,当然也包括目前任何对于特定类型的错误加以的限制。例如,如果连接失败,你可以尝试重新连接或者建立一个到另一个远程节点的连接。

如果你把 ChannelFutureListener 看作是回调的一个更加精细的版本也是可以的。事实上,回调和 Future 是互相补充的机制,它们互相结合,构成了 Netty 本身的关键构件之一。

事件和 ChannelHandler

Netty 使用不同的事件来通知我们状态的改变,这使得我们能够基于已经发生的事件来触发适当的动作。这些动作可能是:记录日志数据转换流控制应用程序逻辑

Netty 是一个网络编程框架,所以事件是按照它们与入站或入站数据流的相关性进行分类的。可能由入站数据或相关的状态更改而触发的事件包括:连接已被激活或者连接失活数据读取用户事件错误事件。出站事件是未来将会触发的某个动作的操作结果,这些动作包括:打开或者关闭到远程节点的连接将数据写到或者冲刷到套接字

每个事件都可以被分给 ChannelHandler 类中的某个用户实现的方法。这是一个很好的将事件驱动范式直接转换为应用程序构件块的例子。

下图展示了一个事件是如何被一个这样的 ChannelHandler 链处理的:

Netty 的 ChannelHandler 为处理器提供了基本的抽象,目前你可以认为每个 ChannelHandler 的实例都类似于一种为了响应特定事件而被执行的回调

总结

Netty 的异步编程模型是建立在 Future 和回调的概念之上的,而将事件派发到 ChannelHandler 的方法则发生在更深的层次上。拦截操作以及高速地转换入站数据和出站数据,都只需要你提供回调或者利用操作所返回的 Future 。

Netty 通过触发事件将 Selector(选择器) 从应用程序中抽象出来,消除了所有本来将需要手动编写的派发代码。在内部,将会为每个 Channel 分配一个 EventLoop ,用于处理这个 Channel 的所有 I/O 事件,包括:注册感兴趣的事件将事件派发给 ChannelHandler安排进一步的动作