使用Java 自带的API开发IO系统时,如果需要对传输的方式进行切换,例如从阻塞传输切换到非阻塞传输, 那么可能会由于两种方式的API不兼容问题需要大面积的修改代码。然而 Netty 则为它所有的传输方式提供了一个通用 API,这使得只需要修改一下对象申明的类型就能完成传输方式的切换,例如从OIO切换到NIO只需要如下操作:
OIO的代码: EventLoopGroup group = new OioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(group) .channel(OioServerSocketChannel.class) ......... } finally { group.shutdownGracefully().sync(); } NIO的代码: EventLoopGroup group = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(group) .channel(NioServerSocketChannel.class) ......... } finally { group.shutdownGracefully().sync(); }
是不是很方便?
传输API的核心Channel,它被用于所有的IO操作,Channel的类图如下所示:
如图所示,每个 Channel 都将会被分配一个 ChannelPipeline 和 ChannelConfig。
ChannelConfig 包含了该 Channel 的所有配置设置,并且支持热更新.
由于 Channel 是独一无二的,所以为了保证顺序将 Channel 声明为 java.lang.
Comparable 的一个子接口。因此,如果两个不同的 Channel 实例都返回了相同的散列码,那么 AbstractChannel 中的 compareTo()方法的实现将会抛出一个 Error.
ChannelPipeline 持有所有将应用于入站和出站数据以及事件的 ChannelHandler 实
例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑,例如:
也可以根据需要通过添加或者移除ChannelHandler实例来修改ChannelPipeline,通过利用Netty的这项能力可以构建出高度灵活的应用程序。
Netty内置了多种类型的传输方式,不同的传输方式有不同的应用场景与支持的协议,目前常见的类型包括以下几种
NIO类型的传输提供了一个所有 I/O 操作的全异步的实现,主要利用了自JAVA 自带 NIO 子系统的选择器API。
Netty 的 NIO 传输基于 Java 提供的异步/非阻塞网络编程的通用抽象。
虽然这保证了 Netty 的非阻塞 API 可以在任何平台上使用,但它也包含了相应的限制,因为 JDK
为了在所有系统上提供相同的功能,必须做出妥协。
Epoll自Linux内核版本 2.5.44(2002)被引入,提供了比旧的POSIX select和poll系统调用更好的性能。如果你的应用程序旨在运行于Linux系统,那么可以考虑利用这个版本的传输;你将发现在高负载下它的性能要优于JDK的NIO实现
Netty 的 OIO 传输实现代表了一种折中:它可以通过常规的传输 API 使用,但是由于它
是建立在 java.net 包的阻塞实现之上的,所以它不是异步的。但是,它仍然非常适合于某些用途。例如,你可能需要移植使用了一些进行阻塞调用的库(如JDBC)的遗留代码,而将逻辑转换为非阻塞的可能也是不切实际的。相反,你可以在短期内使用Netty的OIO传输,然后再将你的代码移植到纯粹的异步传输上。
Netty是如何能够使用和用于异步传输相同的API来支持OIO的呢?
答案就是, Netty利用了SO_TIMEOUT这个Socket标志,它指定了等待一个I/O操作完成的最大毫秒数。如果操作在指定的时间间隔内没有完成, 则将会抛出一个SocketTimeout Exception。 Netty将捕获这个异常并继续处理循环。在EventLoop下一次运行时,它将再次尝试。这实际上也是类似于Netty这样的异步框架能够支持OIO的唯一方式。
Netty 提供了一个 Local 传输,用于在同一个 JVM 中运行的客户端和服务器程序之间的异步通信。
在同一个 JVM 内部的通信,不需要通过网络暴露服务,是
Local 传输的完美用例。这将消除所有真实网络操作的开销,同时仍然使用你的 Netty 代码库。如果随后需要通过网络暴露服务,那么你将只需要把传输改为 NIO 或者 OIO 即可。
如果你想要为自己的 ChannelHandler 实现编
写单元测试,可以考虑使用 Embedded 传输。这既便于测试你的代码,而又不需要创建大
量的模拟(mock)对象。你的类将仍然符合常规的 API 事件流, 保证该 ChannelHandler
在和真实的传输一起使用时能够正确地工作。