流经网络的数据总是具有相同的类型:字节(网络传输——一个帮助我们抽象底层数据传输机制的概念)

Netty为它所有的传输实现提供了一个通用的API,即我们可以将时间花在其他更有成效的事情上。

我们将通过一个案例来对传输进行学习,应用程序只简单地接收连接,向客户端写 “Hi!” ,然后关闭连接。

1、不通过Netty使用OIO和NIO

先介绍JDK API的应用程序的阻塞(OIO)版本和异步(NIO)版本。

Netty实战四之传输

这段代码完全可以处理中等数量的并发客户端,但是随着应用程序变得流行起来,你会发现它并不能很好地伸缩到支撑成千上万的并发连入连接。你决定改用异步网络编程,但是很快就发现异步API是完全不同的,以至于现在你不得不重写你的应用程序。

Netty实战四之传输
Netty实战四之传输

Netty实战四之传输
虽然这段代码所做的事情与之前的版本完全相同,但是代码却截然不同,如果为了用于非阻塞I/O而重新实现这个简单的应用程序,都需要一次完全重写的话,那么不难想象,移植真正复杂的应用程序需要付出什么样的努力!

2、通过Netty使用OIO和NIO

Netty实战四之传输

Netty实战四之传输

3、非阻塞的Netty版本

Netty实战四之传输

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

4、传输API

传输API的核心是interface Channel ,它被用于所有的I/O操作。Channel类的层次结构如图4-1(Channel接口的层次结构)所示。

Netty实战四之传输

如图所示,每个Channel都将会将分配一个ChannelPipeline和ChannelConfig。ChannelConfig包含了该Channel的所有配置设置,并且支持热更新。

由于特定的传输可能具有独特的设置,所以它可能会实现一个ChannelConfig的子类型。

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

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

ChannelHandler的典型用途包括:

-将数据从一种格式转换为另一种格式

-提供异常的通知

-提供Channel变为活动的或者非活动的通知

-提供当Channel注册到EventLoop或者从EventLoop注销时的通知

-提供有关用户自定义事件的通知

拦截过滤器 ChannelPipeline实现了一种常见的设计模式——拦截过滤器(InterceptingFilter)。UNIX管道是另外一个熟悉的例子:多个命令被链接在一起,其中一个命令的输出端将连接到命令行中下一个命令的输入端。

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

Netty实战四之传输

考虑一下写数据并将其冲刷到远程节点这样的常规任务,代码清单4-5演示了使用Channel.writeAndFlush()来实现这一目的。

Netty实战四之传输

Netty的Channel实现是线程安全的,因此你可以存储一个到Channel的引用,并且每当你需要向远程节点写数据时,都可以使用它,即使当时许多线程都在使用它。代码清单4-6展示了一个多线程写数据的简单例子,需要注意的是,消息将会被保证按顺序发送的。

Netty实战四之传输