前言
Netty是一个异步的、基于事件驱动的网络应用程序框架,本文主要介绍Netty的异步机制。
注:关于Netty基础,可以查看 Netty基本介绍 和 线程模型 Netty基本介绍 和 线程模型 这篇文章,本文代码也是基于这篇博文中代码修改的。
一、 同步异步 & 阻塞非阻塞 的区别
1. 关于同步和异步
-
同步:
当发起io资源请求,在io资源尚未就绪时,调用者会主动获取io资源的状态,当资源就绪时,调用者主动获取当前资源就绪的状态,就会返回结果。
-
异步:
当发起io资源请求,在io资源尚未就绪时,调用者会立刻获得返回结果。 当资源就绪时,被调用者主动通过状态、通知来告知调用者,或者通过回调方式告知。
一个常用的例子是:
-
同步:
你想喝开水,在炉灶上烧水(发起io请求),水壶经过一段时间的加热(io资源未就绪),你发现水烧开了,将水壶取下(返回结果)。
-
异步:
你想喝开水,你有一个高级水壶,在炉灶上烧水(发起io请求),水壶配套一个水开警报器,你拿走警报器然后去干别的事,经过一段时间的加热(io资源未就绪),水烧开了,水壶通过警报器给你通知(返回结果)。
2. 关于阻塞和非阻塞
-
阻塞:
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
-
非阻塞:
非阻塞调用指在不能立刻得到结果之前,调用者还可以去干别的事情,不会一直等待当前结果的返回。
举例:
-
阻塞:
你想喝开水,在炉灶上烧水(发起io请求),水壶经过一段时间的加热(io资源未就绪),此时你需要一直等待水壶烧开。
-
非阻塞:
你想喝开水,在炉灶上烧水(发起io请求),水壶经过一段时间的加热(io资源未就绪),此时你不需要一直等待水壶烧开,可以去做别的事情。
3. 总结
结合下面流程图理解,调用者从发起资源请求 到 获取结果。
- 同步异步 关注的是 资源准备就绪后,调用者是如何获知的。
- 阻塞非阻塞 关注的是 资源准备过程中,调用者是否能做别的事。
- 注:同步非阻塞的做法是 资源准备过程中,调用者可以去做别的事,但需要经常回来看看资源是否就绪,当发现资源就绪时,获取结果。
二、 Netty的异步机制
-
Netty 中的 I/O 操作是异步的(Bind、Connect、Write等操作 ),会立刻返回一个 ChannelFuture。
-
调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得IO 操作结果。
-
Netty 的异步模型是建立在 future 和 callback 的之上的。 callback 就是回调。重点说 Future,它的核心思想是:假设一个方法 fun,计算过程可能非常耗时,等 待 fun 返回显然不合适。那么可以在调用 fun 的时候,立马返回一个 Future,后续可以通过 Future 去 监控方法 fun 的处理过程(即 : Future-Listener 机制)
-
Netty中的future
-
future是netty异步操作立刻返回的结果, 可以通过它提供的方法来检测执行是否完成,ChannelFuture 是他的一 个子接口。ChannelFuture 是一个接口 ,可以添加监听器,当监听的事件发生时,就会通知到监听器 。
-
当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取 操作执行的状态, 注册监听函数来执行完成后的操作。
-
常用方法:
方法 介绍 sync 方法 阻塞等待程序结果反回 isDone 方法 判断当前操作是否完成 isSuccess 方法 判断已完成的当前操作是否成功 getCause 方法 获取已完成的当前操作失败的原因 isCancelled 方法 判断已完成的当前操作是否被取消 addListener 方法 注册监听器,当操作已完成(isDone 方法返回完成),将会通知指定的监听 器;如果Future 对象已完成,则通知指定的监听器
-
三、 使用方法
给future添加监听器,监听操作结果
-
修改Server端口绑定处的代码,添加Listener监听端口绑定的状态
ChannelFuture channelFuture = serverBootstrap.bind(9988).sync(); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()){ System.out.println("Server bind success..."); }else { System.out.println("Server bind failed!!!"); } } });
-
修改Client发送数据的自定义Handler代码,添加Listener监听数据发送的状态
//通道就绪 @Override public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception{ ChannelFuture channelFuture = channelHandlerContext.writeAndFlush(Unpooled.copiedBuffer("hello,nettyServer...", CharsetUtil.UTF_8)); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()){ System.out.println("Client send success..."); }else { System.out.println("Client send failed!!!"); } } }); }
-
测试结果: