Netty 传输

末鹿安然 提交于 2020-07-28 10:50:52

Netty 为每种传输的实现都暴露了相同的API,所以无论选用哪一种传输的实现,你的代码都仍然几乎不受影响。在所有的情况下,传输的实现都依赖于 interface Channel 、ChannelPipeline 和 ChannelHandler。

传输 API 的核心是 interface Channel,它被用于所有的I/O操作。

每个 Channel 都将会被分配一个 ChannelPipeline 和 ChannelConfig。ChannelConfig 包含了该 Channel 的所有配置设置,并且支持热更新。由于特定的传输可能具有独特的设置,所以它可能会实现一个 ChannelConfig 的子类型。

由于 Channel 是独一无二的,所以为了保证顺序将 Channel 声明为 java.lang.Comparable 的一个子接口。因此,如果两个不同的 Channel 实例都返回了相同的散列码,那么 AbstractChannel 中 compareTo() 方法的实现将会抛出一个 Error。

ChannelPipeline 持有所有将应用于入站和出战数据以及事件的 ChannelHandler 实例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑。

ChannelHandler 的典型用途包括:

  • 将数据从一种格式转换为另一种格式;
  • 提供异常的通知;
  • 提供 Channel 变为活动的或者非活动的通知;
  • 提供当 Channel 注册到 EventLoop 或者从 EventLoop 注销时的通知;
  • 提供有关用户自定义事件的通知。

ChannelPipeline 实现了一种常见的设计模式——拦截过滤器(Intercepting Filter)。

你也可以根据需要通过添加或者移除 ChannelHandler 实例来修改 ChannelPipeline。通过利用 Netty 的这项能力可以构建出高度灵活的应用程序。例如,每当 STARTTLS 的协议请求时,你可以简单地通过向 ChannelPipeline 添加一个适当地 ChannelHandler (SslHandler) 来按需地支持 STARTTLS 的协议。

Channel 的方法:

  • eventLoop:返回分配给 Channel 的 EventLoop
  • pipeline:返回分配给 Channel 的 ChannelPipeline
  • isActive:如果 Channel 是活动的,则返回 true。活动的意义可能依赖于底层的传输。例如,一个 Socket 传输一旦连接到了远程节点便是活动的,而一个 Datagram 传输一旦被打开便是活动的。
  • localAddress:返回本地的 SocketAddress
  • remoteAddress:返回远程的 SocketAddress
  • write:将数据写到远程节点。这个数据将被传递给 ChannelPipeline ,并且排队直到它被冲刷
  • flush:将之前已写的数据冲刷到底层传输,如一个 Socket
  • writeAndFlush:一个简便的方法,等同于调用 write() 并接着调用 flush()

写数据并将其冲刷到远程节点这样的常规任务。

Channel channel = new NioServerSocketChannel();
// 创建持有要写数据的ByteBuf
ByteBuf buf = Unpooled.copiedBuffer("your data",CharsetUtil.UTF_8);
// 写数据并冲刷它
ChannelFuture cf = channel.writeAndFlush(buf);
// 添加 ChannelFutureListener 以便在写操作完成后接收通知
cf.addListener(new ChannelFutureListener(){
  @Override
  public void operationComplete(ChannelFuture future){
    // 写操作完成,并且没有错误发生
    if(future.isSuccess()){
      System.out.println("Write successful");
    }else{
      System.err.println("Write error");
      future.cause().printStackTrace();
    }
  }
});

Netty 的 Channel 实现是线程安全的,因此你可以存储一个到 Channel 的引用,并且每当你需要向远程节点写数据时,都可以使用它,即使当时许多线程都在使用它。

// 从多个线程使用同一个 Channel
final Channel channel = new NioServerSocketChannel();
// 创建持有要写数据的 ByteBuf
fianl ByteBuf buf = Unpooled.copiedBuffer("your data",CharsetUtil.UTF_8).retain();
// 创建将数据写到 Channel 的 Runnable
Runnable writer = new Runnable(){
  @Override
  public void run(){
    channel.writeAndFlush(buf.duplicate());
  }
};
// 获取到线程池 Executor 的引用
Executor executor = Executors.newCachedThreadPool();
// write in one thread
// 递交写任务给线程池以便在某个线程中执行
executor.execute(writer);
// write in another thread
// 递交另一个写任务以便在另一个线程中执行
executor.execute(writer);





内置的传输

Netty 内置了一些可开箱即用的传输。因为并不是它们所有的传输都支持每一种协议,所以你必须选择一个和你的应用程序所使用的协议相容的传输。

Netty 所提供的传输:

  • NIO  :  io.netty.channel.socket.nio  使用 java.nio.channels 包作为基础——基于选择器的方式
  • Epoll  :  io.netty.channel.epoll  由 JNI 驱动的 epoll() 和非阻塞 IO。这个传输支持只有在 Linux 上可用的多种特性,如 SO_REUSEPORT,比 NIO 传输更快,而且是完全非阻塞的
  • OIO  :  io.netty.channel.socket.oio  使用 java.net 包作为基础——使用阻塞流
  • Local  :  io.netty.channel.local  可以在 VM 内部通过管道进行通信的本地传输
  • Embedded  :  io.netty.channel.embedded Embedded 传输,允许使用 ChannelHandler 而又不需要一个真正的基于网络的传输。这在测试你的 ChannelHandler 实现时非常有用

 

 

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!