BIO:
在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式。
传统的客户端服务器就是采用此种IO方式,一个socket链接对应一个线程,服务器中的建立连接 “serverSocket.accept()”会一直阻塞,直到有连接接入。
NIO:
在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。
可以使用setsockopt()方法配置套接字,以便读/写调用在没有数据的时候立即返回,也就是说:如果是一个阻塞调用应该已经被阻塞了;可以使用操作系统的事件通知API注册一组非阻塞套接字,以确定它们中是否有任何的套接字已经有数据可供读写。
class java.nio.channels.Selector 是Java 的非阻塞I/O 实现的关键。它使用了事件通知API以确定在一组非阻塞套接字中有哪些已经就绪能够进行I/O 相关的操作。
Netty——事件和异步驱动
一个既是异步的又是事件驱动的系统会表现出一种特殊的、对我们来说极具价值的行为:它可以以任意的顺序响应在任意的时间点产生的事件。
Netty的核心组件:
- channel:实体的开放连接,可以把Channel 看作是传入(入站)或者传出(出站)数据的载体。
- 回调:Netty在内部使用了回调来处理事件;当一个回调被触发时,相关的事件可以被interface-ChannelHandler
的实现处理。 - Future:Netty提供了自己的实现——ChannelFulture,用于在执行异步操作的时候使用。Fulture需要添加监听器ChannelFutureListener,其可以看作是回调的一个更加精细的版本。每个Netty的出站IO操作都会返回一个ChannelFuture.
- 事件和channelHandler:首先Netty中的事件是按照它们与入站或出站数据流的相关性进行分类的。下图为事件和channelHandler的关系。channelHandler就是事件处理器的抽象。
Netty核心组件间的关系,事件通过channel被派发给channelHandler,然后channelHandler对事件的处理和结果返回通过回调和Future来完成。对于事件的派发等相关操作被封装到EventLoop.
EventLoop 本身只由一个线程驱动,其处理了一个Channel 的所有I/O 事件,并且在该EventLoop 的整个生命周期内都不会改变。这个简单而强大的设计消除了你可能有的在ChannelHandler 实现中需要进行同步的任何顾虑。
Netty编写的服务器必备:
- 至少一个——ChannelHandler,该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑。
- 引导—这是配置服务器的启动代码。至少,它会将服务器绑定到它要监听连接请求的端口上。
ChannelHandler 和业务逻辑:
ChannelHandler,它是一个接口族的父接口,它的实现负责接收并响应事件通知。在Netty 应用程序中,所有的数据处理逻辑都包含在这些核心抽象的实现中。
实现ChannelInboundHandler接口的Echo服务器,我们感兴趣的方法为:
- channelRead()—对于每个传入的消息都要调用;
- channelReadComplete()—通知ChannelInboundHandler 最后一次对channelRead()的调用是当前批量读取中的最后一条消息;
- exceptionCaught()—在读取操作期间,有异常抛出时会调用。
Netty的组件和设计
了解Netty中类的抽象:
- Channel—Socket;
- EventLoop—控制流、多线程处理、并发;
- ChannelFuture—异步通知。
三者的关系:
- EventLoopGroup包含一个或者多个EventLoop;
- 一个EventLoop在它的生命周期内只和一个Thread绑定;
- 所有由EventLoop处理的I/O事件都将在它专有Thread上被处理;
- 一个Channel在它的生命周期内只注册于一个EventLoop;
- 一个EventLoop可能会被分配给一个或多Channel。
ChannelHandler 和ChannelPipeline
- ChannelHandlerNetty的主要组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器。
- ChannelPipeline 提供了ChannelHandler 链的容器,并定义了用于在该链上传播入站和出站事件流的API。当Channel 被创建时,它会被自动地分配到它专属的ChannelPipeline。
二者的关系:
使得事件流经ChannelPipeline 是ChannelHandler 的工作。实际上,被我们称为ChannelPipeline 的是这些ChannelHandler 的编排顺序。
注意:当ChannelHandler 被添加到ChannelPipeline 时,它将会被分配一个ChannelHandlerContext,其代表了ChannelHandler 和ChannelPipeline 之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。