kafka网络客户端中的异步NIO原理
开始认为kafka生产端的future应用是线程中的异步操作, 直到后来看到消费端主要到只有一个线程的情况下収future的使用
NIO.2,主要包括新的:
- 异步 I/O(简称 AIO);
- Multicase 多播;
- Stream Control Transport Protocol(SCTP);
- 文件系统 API;
- 以及一些 I/O API 的更新,例如:java.io.File.toPath,NetworkChannel 的完整抽象,等等。
AIO 的 I/O 操作,有两种方式的 API 可以进行:
- Future 方式;
- Callback 方式。
下面我们分别对这两种方式的 API 进行举例说明。
Future 方式
Future 方式:即提交一个 I/O 操作请求,返回一个 Future。然后您可以对 Future 进行检查,确定它是否完成,或者阻塞 IO 操作直到操作正常完成或者超时异常。使用 Future 方式很简单,比较典型的代码通常像清单 1 所示。
清单 1. 使用 Future 方式的代码示例
AsynchronousSocketChannel ch = AsynchronousSocketChannel.open();
// 连接远程服务器,等待连接完成或者失败
Future<Void> result = ch.connect(remote);
// 进行其他工作,例如,连接后的准备环境,f.e.
//prepareForConnection();
//Future 返回 null 表示连接成功
if(result.get()!=null){
// 连接失败,清理刚才准备好的环境,f.e.
//clearPreparation();
return;
}
// 网络连接正常建立
...
ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
// 进行读操作
Future<Integer> result = ch.read(buffer);
// 此时可以进行其他工作,f.e.
//prepareLocalFile();
// 然后等待读操作完成
try {
int bytesRead = result.get();
if(bytesRead==-1){
// 返回 -1 表示没有数据了而且通道已经结束,即远程服务器正常关闭连接。
//clear();
return;
}
// 处理读到的内容,例如,写入本地文件,f.e.
//writeToLocolFile(buffer);
} catch (ExecutionExecption x) {
//failed
}
需要注意的是,因为 Future.get()
是同步的,所以如果不仔细考虑使用场合,使用 Future 方式可能很容易进入完全同步的编程模式,从而使得异步操作成为一个摆设。如果这样,那么原来旧版本的 Socket API 便可以完全胜任,大可不必使用异步 I/O。
Callback 方式
Callback 方式:即提交一个 I/O 操作请求,并且指定一个 CompletionHandler
。当异步 I/O 操作完成时,便发送一个通知,此时这个 CompletionHandler 对象的 completed 或者 failed 方法将会被调用,样例代码如清单 2 所示。
清单 2. 完成处理接口
public interface CompletionHandler<V,A> {
// 当操作完成后被调用,result 参数表示操作结果,
//attachment 参数表示提交操作请求时的参数。
void completed(V result, A attachment);
// 当操作失败是调用,exc 参数表示失败原因。attachment 参数同上。
void failed(Throwable exc, A attachment);
}
关于 Attachment 参数
Attachment 参数是不是看着十分眼熟呢?是的,NIO 中也使用类似的方法。当然 I/O 操作是不会对这个参数进行任何操作的,可以用于在不同的 CompletionHandler 对象之间进行通信。
- V表示结果值的类型。对于异步网络通道的读写操作而言,这个结果值 V 都是整数类型,表示已经操作的卦数,如果是 -1,NIO.2 内核实现保证传递的
ByteBuffer
参数不会有变化。 - A表示关联到 I/O 操作的对象的类型。用于传递操作环境。通常会封装一个连接环境。
- 如果成功则 completed 方法被调用。如果失败则 failed 方法被调用。
准备好 CompletionHandler 之后,如何使用 CompletionHandler 呢? AIO 提供四种类型的异步通道以及不同的 I/O 操作接受一个 CompletionHandler 对象,它们分别是:
- AsynchronousSocketChannel:connect,read,write代码段 小部件
- AsynchronousFileChannel:lock,read,write
- AsynchronousServerSocketChannel:accept
- AsynchronousDatagramChannel:read,write,send,receive
本文重点关注 AsynchronousSocketChannel 的使用,首先简单浏览一下该类型的 API。
AsynchronousSocketChannel
public abstract class AsynchronousSocketChannel
implements AsynchronousByteChannel, NetworkChannel
创建一个异步网络通道,并且绑定到一个默认组。
public static AsynchronousSocketChannel open() throws IOException
将异步网络通道连接到远程服务器,使用指定的 CompletionHandler 听候完成通知。
public abstract <A> void connect(SocketAddress remote, A attachment,
CompletionHandler<Void,? super A> handler)
从异步网络通道读取数据到指定的缓冲区,使用指定的 CompletionHandler 听候完成通知。
public final <A> void read(ByteBuffer dst, A attachment,
CompletionHandler<Integer,? super A> handler)
向异步网络通道写缓冲区中的数据,使用指定的 CompletionHandler 听候完成通知。
public final <A> void write(ByteBuffer src,A attachment,
CompletionHandler<Integer,? super A> handler)
参考: https://www.ibm.com/developerworks/cn/java/j-lo-nio2/index.html