同步/异步、阻塞/非阻塞
-
同步
与异步
: 调用者是否等待结果
返回- 同步:等待结果响应
- 异步:不等待结果返回,继续往下走
-
阻塞
与非阻塞
:线程是否阻塞
IO的方式:
- 同步阻塞的
BIO
(Blocking I/O)、
BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
- 同步非阻塞的
NIO
(non-blocking I/O 、 new io) :
NIO (New I/O):同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?
如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
- 异步非阻塞的
AIO
(Asynchronous I/O)。
AIO ( Asynchronous I/O):异步非阻塞I/O模型。异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。
对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
Bootstrap、ServerBootstrap
NioEventLoopGroup
参考:
Netty是一个
异步事件驱动
的NIO网络通信框架
,Netty的所有IO操作都是异步非阻塞
的。
Netty 实际上是使用 Threads(多线程)处理 I/O 事件,熟悉多线程编程的读者可能会需要关注同步代码。但是这么做不好,因为同步会影响程序的性能,Netty 的设计保证程序处理事件不会有同步。
Channel :表示一个连接
Channel
,一个客户端与服务器通信的通道,表示一个连接,可以理解为每一个请求,就是一个Channel。
Channel和IO中的Stream(流)是差不多一个等级的。
只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
NIO中的Channel的主要实现有 :
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
底层网络传输 API 必须提供给应用 I/O操作的接口,如读,写,连接,绑定等等。对于我们来说,这是结构几乎总是会成为一个“socket”。 Netty 中的接口 Channel 定义了与 socket 丰富交互的操作集:bind, close, config, connect, isActive, isOpen, isWritable, read, write 等等。 Netty 提供大量的 Channel 实现来专门使用。这些包括 AbstractChannel,AbstractNioByteChannel,AbstractNioChannel,EmbeddedChannel, LocalServerChannel,NioSocketChannel 等等。
ChannelHandler : 处理逻辑业务
ChannelHandler
,业务逻辑处理器,核心处理业务就在这里,用于处理业务请求。
分为ChannelInboundHandler和ChannelOutboundHandler
- ChannelInboundHandler,输入数据处理器
- ChannelOutboundHandler,输出业务处理器
通常情况下,业务逻辑都是存在于ChannelHandler之中.
ChannelHandler 支持很多协议,并且提供用于数据处理的容器。我们已经知道 ChannelHandler 由特定事件触发。 ChannelHandler 可专用于几乎所有的动作,包括将一个对象转为字节(或相反),执行过程中抛出的异常处理。
常用的一个接口是 ChannelInboundHandler,这个类型接收到入站事件(包括接收到的数据)可以处理应用程序逻辑。当你需要提供响应时,你也可以从 ChannelInboundHandler 冲刷数据。一句话,业务逻辑经常存活于一个或者多个 ChannelInboundHandler。
ChannelPipeline : 保存ChannelHandler和ChannelHandlerContext
ChannelPipeline
, 用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。
ChannelPipeline 提供了一个容器给 ChannelHandler 链并提供了一个API 用于管理沿着链入站和出站事件的流动。每个 Channel 都有自己的ChannelPipeline,当 Channel 创建时自动创建的。 ChannelHandler 是如何安装在 ChannelPipeline? 主要是实现了ChannelHandler 的抽象 ChannelInitializer。ChannelInitializer子类 通过 ServerBootstrap 进行注册。当它的方法 initChannel() 被调用时,这个对象将安装自定义的 ChannelHandler 集到 pipeline。当这个操作完成时,ChannelInitializer 子类则 从 ChannelPipeline 自动删除自身。
ChannelHandlerContext : 上下文
ChannelHandlerContext
,通信管道的上下文,用于传输业务数据
他们的交互流程是:
1、事件传递给 ChannelPipeline
的第一个 ChannelHandler
2、ChannelHandler
通过关联的 ChannelHandlerContext
传递事件给 ChannelPipeline
中的 下一个ChannelHandler
3、ChannelHandler
通过关联的 ChannelHandlerContext
传递事件给 ChannelPipeline
中的 下一个ChannelHandler
Bootstrap : 建造服务
bootstrap 是我们的 netty 服务建造者, 用于定义启动我们的 netty 服务。
- ServerBootstrap 用于定义服务端的netty 程序,
- Bootstrap 用于定义客户端的 netty 程序。
Netty 应用程序通过设置 bootstrap(引导)类的开始,该类提供了一个 用于应用程序网络层配置的容器。
EventLoop : 线程池
EventLoopGroup 是 Netty 内部定一个一个线程池结构, 用于执行来自 netty 内部的事件监听与逻辑处理。
在 EventLoopGroup 中每一个线程为 EventLoop 结构。每个 Channel 会关联一个 EventLoop,用于执行这个 Channel 触发的任务。
EventLoop 用于处理 Channel 的 I/O 操作。一个单一的 EventLoop通常会处理多个 Channel 事件。一个 EventLoopGroup 可以含有多于一个的 EventLoop 和 提供了一种迭代用于检索清单中的下一个。
ChannelFuture : 获取结果
Netty 所有的 I/O 操作都是异步。因为一个操作可能无法立即返回,我们需要有一种方法在以后确定它的结果。出于这个目的,Netty 提供了接口 ChannelFuture,它的 addListener 方法注册了一个 ChannelFutureListener ,当操作完成时,可以被通知(不管成功与否)。
ChannelInitializer
一个特殊的ChannelInboundHandler
,它提供了一种简单的方法来初始化Channel一旦它被注册到它的EventLoop 。 实现最常用于Bootstrap.handler(ChannelHandler)
、 ServerBootstrap.handler(ChannelHandler)
和ServerBootstrap.childHandler(ChannelHandler)
的上下文中,以设置Channel
的ChannelPipeline
。
public class MyChannelInitializer extends ChannelInitializer {
public void initChannel(Channel channel) {
channel.pipeline().addLast("myHandler", new MyHandler());
}
}
ServerBootstrap bootstrap = ...;
...
bootstrap.childHandler(new MyChannelInitializer());
...
SimpleChannelInboundHandler
和 ChannelInboundHandlerAdapter
区别:
netty发送和接收数据handler处理器 主要是继承 SimpleChannelInboundHandler
和 ChannelInboundHandlerAdapter
一般用netty来发送和接收数据都会继承SimpleChannelInboundHandler
和ChannelInboundHandlerAdapter
这两个类,那么这两个到底有什么区别呢?
其实用这两个抽象类是有讲究的,在客户端
的业务Handler继承的是SimpleChannelInboundHandler
,而在服务器端
继承的是ChannelInboundHandlerAdapter
。
最主要的区别就是SimpleChannelInboundHandler
在接收到数据后会自动release掉数据占用的Bytebuffer资源(自动调用Bytebuffer.release())。
而为何服务器端不能用呢,因为我们想让服务器把客户端请求的数据发送回去,而服务器端有可能在channelRead方法返回前还没有写完数据,因此不能让它自动release。
-
SimpleChannelInboundHandler
是ChannelInboundHandlerAdapter
的子类 : -
继承
ChannelInboundHandlerAdapter
后,即可收到消息并处理
.
public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter {
private final TypeParameterMatcher matcher;
private final boolean autoRelease;
/**
* see {@link #SimpleChannelInboundHandler(boolean)} with {@code true} as boolean parameter.
*/
protected SimpleChannelInboundHandler() {
this(true);
}
/**
* Create a new instance which will try to detect the types to match out of the type parameter of the class.
*
* @param autoRelease {@code true} if handled messages should be released automatically by passing them to
* {@link ReferenceCountUtil#release(Object)}.
*/
protected SimpleChannelInboundHandler(boolean autoRelease) {
matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
this.autoRelease = autoRelease;
}
}
ChannelInboundHandlerAdapter 的方法:
public class EchoServerHandler extends ChannelInboundHandlerAdapter{
public void channelRegistered(ChannelHandlerContext ctx) {
System.out.println("注册");
}
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("激活");
}
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("断开");
}
public void channelUnregistered(ChannelHandlerContext ctx) {
System.out.println("注销");
}
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("读取消息");
}
public void channelReadComplete(ChannelHandlerContext ctx) {
System.out.println("消息读取完成");
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
System.out.println("用户事件");
}
public void channelWritabilityChanged(ChannelHandlerContext ctx){
System.out.println("可写状态变更为"+ctx.channel().isWritable());
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("发生异常");
}
}