vlambda博客
学习文章列表

Netty的核心构成块


描述:在学习Netty做的学习笔记,如有不对请留言指出,谢谢!

Netty的核心构成块

(Channel、回调、Future、事件和ChannelHandler)这些构建块代表了不同类型的构造:资源、逻辑以及通知。你的应用程序将使用它们来访问网络以及流经网络的数据

1、Channel

在java NIO就有这个,其实就是java NIO的基本构造。他代表到一个实体(比如一个硬件,一个文件等)

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

2、Callback(回调)

可以从名称中来进行理解,回调就是一个方法,一个指向已经被提供给另一个方法的引用,比如在一个操作完成后需要通知相关信息常用方式之一

Netty在内部使用了回调来处理事件;当一个回调被触发时,相关的事件被interface-ChannnelHandler的实现处理。

举个栗子:

public class ConnectHandler extends ChannelInboundHandlerAdapter {
   @Override
   public void channelActive(ChannelHandlerContext ctx)throws Exception {
      // 当一个新的连接已经被建立时,channelActive(ChannelHandlerContext)将会被调用
     System.out.println("Client " + ctx.channel().remoteAddress() + " connected");
   }
}


3、Future

该类提供了进入异步任务时,可以通过该类将在未来的某个时刻完成的任务,并提供对其结果的访问

JDK中预置了该类interface java.util.concurrent.Future ,但是所提供的实现,只允许手动检查对应操作是否完成,或者一直阻塞直到异步任务结束获取到结果,这么索呢?就是非常繁琐,所以Netty提供了对它的实现ChannelFuture,用于在执行异步操作的时候使用

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

每个Netty的传出都返回一个ChannelFuture,反之可得,传入都不会阻塞,证实Netty是完全基于异步和事件驱动

不多说举个栗子

Channel channel = ...;
// 异步地连接到远程节点
ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));

这里,connect()方法将会直接返回,而不会阻塞,该调用将会在后台完成。这究竟什么时候会发生则取决于若干的因素,但这个关注点已经从代码中抽象出来了。因为线程不用阻塞以等待对应的操作完成,所以它可以同时做其他的工作,从而更加有效地利用资源。

上面代码告诉你如何利用ChannelFuture

1、连接到远程节点上

2、注册一个新的ChannelFutureListener到对connect方法调用到所返回的ChannelFuture

当该ChannelFutureListener被通知已经被建立的时候,要检查对应的状态,如果操作是成功的,那么将数据写到该channel,否则要从ChannelFuture中检索对应的Throwable错误

Channel channel = ...;

// 异步地连接到远程节点
ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));
//注册一个ChannelFutureListener,以便在操作完成时获得通知
future.addListener(new ChannelFutureListener() {
   //检查操作的状态
   @Override
   public void operationComplete(ChannelFuture future) {
    if (future.isSuccess()){
       //如果操作是成功的,则创建一个ByteBuf以持有数据
       ByteBuf buffer = Unpooled.copiedBuffer("Hello",Charset.defaultCharset());
//将数据异步地发送到远程节点。
       ChannelFuture wf = future.channel().writeAndFlush(buffer);
//返回一个ChannelFuture
      //省略代码 ....
     } else {
//如果发生错误,则访问描述原因的Throwable
       Throwable cause = future.cause();
       cause.printStackTrace();
     }
   }
});

需要注意的是,对错误的处理完全取决于你、目标,当然也包括目前任何对于特定类型的错误加以的限制

例如,如果连接失败,你可以尝试重新连接或者建立一个到另一个远程节点的连接。

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

4、事件和ChannelHandler

Netty使用不同的事件来通知我们状态或者是操作的状态,这使得我们能够基于已经发生的事件来进行应当的业务操作,这些业务操作可能是:

记录日志

数据转换

流控制

等等的应用程序逻辑

Netty是一个网络编程框架,所以事件是按照他们与传入或者传出的数据流相关性进行分类的,可能又传入触发的世界包含有:

错误事件

用户事件

数据读取

连接已经失活

....等

传出事件是未来将会触发的某个动作的操作结果,这些动作包含

打开远程节点的连接

将数据写入或者冲刷到套接字

简单来说每个事件都可以分发给ChannelHandler类中

图解:

Netty的ChannelHandler为处理器提供了基本的抽象,如图,我们会在适当的时候对ChannelHandler进行更多的说明,但是目前你可以认为每个Channel-Handler的实例都类似于一种为了响应特定事件而被执行的回调

Netty提供了大量预定可以开箱即用,拎包入住的Channelhandler实现,包括用于各种协议(Http、SSL/TLS)的ChannelHandler。在内部,Channelhandler自己也使用了事件和Future使它们也成为你的应用程序将使用相同抽象的消费者。

放在一起

1.Future、回调和ChannelHandler

Netty异步编程模型基于Future和回调概念之上的,而将事件派发到Channelhandler的方法发送在更深的层次上。结合在一起这些元素就提供了一个处理的环境,使你的应用程序逻辑可言独立于任何网络操作相关的顾虑而独立的演变,这就是Netty设计方式的一个关键目标

2.选择器、事件和EventLoop

Netty通过触发事件将Selector从应用程序中抽象出来,消除了所有本来将需要手动编写的派发代码,在内部会为每个Channel分配一个EventLoop,用于处理所有的事件,包括:

注册感兴趣的事件

将事件派发给channelhandler

安排进一步动作

eventLoop本身就是一个线程驱动,其处理一个Channel的所有I/O事件,并且在该EventLoop的整个生命周期内不会改变,这个简单而强大的设计消除了你可能有的Channel实现中西药进行同步的任何顾虑,因此你更可以专注于提供正确的逻辑,用来在感兴趣的数据要处理的时候执行,如同Netty的线程模型,该API是紧凑的