目录
传输API
内置的传输
NIO——非阻塞IO
Epoll——用于Linux的本地非阻塞传输
OIO——旧的阻塞IO
用于JVM内部通信的Local传输
Embedded传输
传输支持的协议
传输API
Netty传输api的核心是Channel,它被用于所有的IO操作。Channel类的层次结构如下:
如图所示,每个Channel类会被分配一个ChannelPipeline和ChannelConfig。ChannelConfig包含了该Channel的所有配置设置,并且支持热更新。由于特定的传输可能具有独特的设置,所以它可能会实现一个ChannelConfig的子类。
由于Channel是独一无二的,所以为了保证顺序将Channel声明为java.lang.Comparable的一个子接口。如果两个不同的Channel实例返回了相同的散列码,那么AbstractChannel中的compareTo方法的实现将抛出一个Error。
ChannelPipeline持有所有将应用于入站和出站数据以及事件的ChannelHandler实例,这些ChannelHandler实现了应用程序用于处理状态变化以及数据处理的逻辑。
ChannelHandler的典型用途包括:
- 将数据从一种格式转换为另一种格式
- 提供异常的通知
- 提供Channel变为活动的或非活动的通知
- 提供当Channel注册到EventLoop或者从EventLoop注销时的通知
- 提供有关用户自定义事件的通知
你也可以根据需要通过添加或者移除ChannelHandler实例来修改ChannelPipeline。通过利用Netty的这项能力能够构建出高度灵活的应用程序。除了访问所分配的ChannelPipeline和ChannelConfig之外,也可以利用Channel的其他方法,其中最重要的列举如下。
方法名 描述 eventLoop 返回分配给Channel的EventLoop pipeline 返回分配给Channel的ChannelPipeline isActive 如果Channel是活动,则返回true。活动的意义可能依赖于底层的传输,例如一个Socket传输连接到了远程节点便是活动的,而Datagram传输打开便是活动的。 localAddress 返回本地的SocketAddress remoteAddress 返回远程的SocketAddress write 将数据写到远程节点。这个数据将被传输给ChannelPipeline,并且排队直到它被冲刷。 flush 将之前已写的数据冲刷到底层传输,如一个Socekt writeAndFlush 等同于write然后flush Netty所提供的广泛功能只依赖于少量的接口。这意味着你可以对你的应用程序逻辑进行重大的修改,而又无需大规模地重构代码库。
考虑一下写数据并将其冲刷到远程节点这样的常规任务。
Channel = ... ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8); //创建持有要写数据的ByteBuf ChannelFuture cf = buf.writeAndFlush(buf); //写数据并冲刷它 //添加ChannelFutureListener以便在写操作完成后收到通知 cf.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) { if (future.isSuccess()) { } else { future.cause().printStackTrace(); } } });
Netty的Channel实现是线程安全的,因此你可以存储一个到Channel的引用,并且每当你需要向远程节点写数据时,都可以使用它,即使当时许多线程都在使用它。需要注意的是,消息将会被保证按顺序发送。
内置的传输
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实现时非常有用 NIO——非阻塞IO
NIO提供了所有IO操作的全异步的实现,它利用了自NIO子系统被引入JDK 1.4时便可用的基于选择器的API。选择器背后的基本概念是充当一个注册表,在那里你将可以请求在Channel的状态变化时得到通知。可能的状态变化有:
- 新的Channel已被接受并且就绪
- Channel连接已经完成
- Channel有已经就绪的可供读取的数据
- Channel可用于写数据
选择器运行在一个检查状态变化并对其作出响应的线程上,在应用程序对状态的改变作出响应后,选择器将会被重置,并将重复这个过程。下表中的常量值代表了由class java.nio.channels.SelectionKey定义的位模式。这些位模式可以组合起来定义一组应用程序正在请求通知的状态变化集。
名称 描述 OP_ACCPET 请求在接受新连接并创建Channel时获得通知 OP_CONNECT 请求在建立一个连接时获得通知 OP_READ 请求当数据已经就绪,可以从Channel中读取时获得通知 OP_WRITE 请求当可以向Channel中写更多数据时获得通知。这处理了套接字缓冲区被完全填满时的情况,这种情况通常发生在数据的发送速度比远程节点可处理的速度更快的时候 对Netty的所有传输实现都共有的用户级别API完全地隐藏了这些NIO的内部细节,下图展示了该处理过程。
Epoll——用于Linux的本地非阻塞传输
Netty为Linux提供了一组NIO API,其以一种和它本身的设计更加一致的方式使用epoll,并且以一种更加轻量的方式使用中断。如果你的应用程序旨在运行于Linux,那么请考虑利用这个版本的传输,你将发现在高负载下它的性能要优于JDK的NIO实现。
OIO——旧的阻塞IO
Netty的OIO传输实现代表了一种折中:它可以通过常规的传输API使用,但是由于它是建立在java.net包的阻塞实现之上的,所以它不是异步的。但是,它仍然非常适用于某些用途。
例如,你可能需要移植使用了一些进行阻塞调用的库的遗留代码,而将逻辑转换为非阻塞的可能也是不切实际的。相反,你可以在短期内使用Netty的OIO传输,然后再将你的代码移植到纯粹的异步传输上。让我们来看一看怎么做。
在java.net API中,你通常会有一个用来接受到达正在监听的ServerSocket的新连接的线程。这是必需的,因为某个指定套接字上的任何IO操作在任意的时间点上都可能会阻塞。使用单个线程来处理多个套接字,很容易导致一个套接字上的阻塞操作也捆绑了其他套接字。
用于JVM内部通信的Local传输
Netty提供了一个Local传输,用于在同一个JVM中运行的客户端和服务端程序之间的异步通信。同样,这个传输也支持对于所有Netty传输实现都共有的API。
在这个传输中,和服务器Channel相关联的SocketAddress并没有绑定物理网络地址;相反,只要服务器还在运行,它就会被存储在注册表里,并在Channel关闭时注销。因为这个传输并不接受真正的网络流量,所以它并不能够和其他传输实现进行互操作。因此,客户端希望连接到(在同一个JVM中)使用了这个传输的服务器端时也必须使用它。除了这个限制,它的使用方式和其他传输一模一样。
Embedded传输
Netty提供了一种额外的传输,使得你可以将一组ChannelHandler作为帮助器类嵌入到其他的ChannelHandler内部。通过这种方式,你将可以扩展一个ChannelHandler的功能,而又不需要修改其内部代码。如果你想要为自己的ChannelHandler实现编写单元测试,那么请考虑使用Embedded传输。这既便于测试你的代码,又不需要创建大量的模拟(Mock)对象。你的类将仍然符合常规的API事件流,保证该ChannelHandler在和真实的传输在一起使用时能够正确地工作。
不足为奇的是,Embedded传输的关键是一个被称为EmbeddedChannel的具体的Channel实现。
传输支持的协议
传输 TCP UDP SCTP UDT NIO √ √ √ √ Epoll √ √ × × OIO √ √ √ √
还没有评论,来说两句吧...