kafka中的回调函数
kafka客户端中使用了很多的回调方式处理请求。基本思路是将回调函数暂存到ClientRequest中,而ClientRequest会暂存到inFlightRequests中,当返回response的时候,从inFlightRequests中读取对应的ClientRequest,并调用request中的回调函数完成处理。
inFlightRequests是请求和响应处理的桥梁.
1. 接口和抽象类
无论是producer还是consumer,回调函数类都是实现了RequestCompletionHandler接口。
public interface RequestCompletionHandler {
public void onComplete(ClientResponse response);
}
consumer的回调函数类不但实现了RequestCompletionHandler,还继承了RequestFuture。RequestFuture是一个有状态的类,在调用中会设置响应的状态,可以持有RequestFuture的引用,用来判断请求的状态。
public class RequestFuture<T> {
private boolean isDone = false;
private T value;
private RuntimeException exception;
private List<RequestFutureListener<T>> listeners = new ArrayList<>();
// 省略其他方法
}
2. producer
producer是在sender线程中创建的ClientRequest,如下:
private List<ClientRequest> createProduceRequests(Map<Integer, List<RecordBatch>> collated, long now) {
List<ClientRequest> requests = new ArrayList<ClientRequest>(collated.size());
for (Map.Entry<Integer, List<RecordBatch>> entry : collated.entrySet())
requests.add(produceRequest(now, entry.getKey(), acks, requestTimeout, entry.getValue()));
return requests;
}
// 创建request
private ClientRequest produceRequest(long now, int destination, short acks, int timeout, List<RecordBatch> batches) {
Map<TopicPartition, ByteBuffer> produceRecordsByPartition = new HashMap<TopicPartition, ByteBuffer>(batches.size());
final Map<TopicPartition, RecordBatch> recordsByPartition = new HashMap<TopicPartition, RecordBatch>(batches.size());
for (RecordBatch batch : batches) {
TopicPartition tp = batch.topicPartition;
produceRecordsByPartition.put(tp, batch.records.buffer());
recordsByPartition.put(tp, batch);
}
ProduceRequest request = new ProduceRequest(acks, timeout, produceRecordsByPartition);
RequestSend send = new RequestSend(Integer.toString(destination),
this.client.nextRequestHeader(ApiKeys.PRODUCE),
request.toStruct());
// 回调函数
RequestCompletionHandler callback = new RequestCompletionHandler() {
public void onComplete(ClientResponse response) {
handleProduceResponse(response, recordsByPartition, time.milliseconds());
}
};
// 回调函数保存到request中, 然后request被保存到了inFlightRequests
return new ClientRequest(now, acks != 0, send, callback);
}
在NetworkClient#poll(..)最后会处理会调用对应的回调函数
public List<ClientResponse> poll(long timeout, long now) {
long metadataTimeout = metadataUpdater.maybeUpdate(now);
try {
this.selector.poll(Utils.min(timeout, metadataTimeout, requestTimeoutMs));
} catch (IOException e) {
log.error("Unexpected error during I/O", e);
}
// process completed actions
long updatedNow = this.time.milliseconds();
List<ClientResponse> responses = new ArrayList<>();
handleCompletedSends(responses, updatedNow);
handleCompletedReceives(responses, updatedNow);
handleDisconnections(responses, updatedNow);
handleConnections();
handleTimedOutRequests(responses, updatedNow);
// invoke callbacks
for (ClientResponse response : responses) { // response中封装了request中的回调函数
if (response.request().hasCallback()) {
try {
response.request().callback().onComplete(response); //调用回调函数
} catch (Exception e) {
log.error("Uncaught error in request completion:", e);
}
}
}
return responses;
}
3. Consumer
consumer使用回调函数和producer使用方式类似,但是比producer复杂一些。前面说了Consumer的回调函数不但实现了RequestCompletionHandler,还继承了RequestFuture。
public static class RequestFutureCompletionHandler
extends RequestFuture<ClientResponse>
implements RequestCompletionHandler {
@Override
public void onComplete(ClientResponse response) {
if (response.wasDisconnected()) {
ClientRequest request = response.request();
RequestSend send = request.request();
ApiKeys api = ApiKeys.forId(send.header().apiKey());
int correlation = send.header().correlationId();
log.debug("Cancelled {} request {} with correlation id {} due to node {} being disconnected",
api, request, correlation, send.destination());
raise(DisconnectException.INSTANCE);
} else {
complete(response); // 关键, complete方法会设置RequestFuture的状态
}
}
}
}
public void complete(T value) { // 设置RequestFuture状态
if (isDone)
throw new IllegalStateException("Invalid attempt to complete a request future which is already complete");
this.value = value;
this.isDone = true;
fireSuccess(); // 循环调用RequestFuture中的listeners
}
private void fireSuccess() {
for (RequestFutureListener<T> listener : listeners)
listener.onSuccess(value);
}
private void fireFailure() {
for (RequestFutureListener<T> listener : listeners)
listener.onFailure(exception);
}
与producer类似,请求被放到一个map中,不过名字是unsent。如下ConsumerNetworkClient#send(..):
public RequestFuture<ClientResponse> send(Node node,
ApiKeys api,
AbstractRequest request) {
long now = time.milliseconds();
RequestFutureCompletionHandler future = new RequestFutureCompletionHandler(); // 回调函数
RequestHeader header = client.nextRequestHeader(api);
RequestSend send = new RequestSend(node.idString(), header, request.toStruct());
put(node, new ClientRequest(now, true, send, future)); // request方法哦unsent中
return future; // 并返回回调函数类的引用
}
在调用ConsumerNetworkClient#send(..)后又紧接着调用了Future#compose(..)。如下:
private RequestFuture<Void> sendGroupCoordinatorRequest() {
Node node = this.client.leastLoadedNode();
if (node == null) {
return RequestFuture.noBrokersAvailable();
} else {
log.debug("Sending coordinator request for group {} to broker {}", groupId, node);
GroupCoordinatorRequest metadataRequest = new GroupCoordinatorRequest(this.groupId);
return client.send(node, ApiKeys.GROUP_COORDINATOR, metadataRequest) // send后返回FutureRequest,然后又调用compose方法
.compose(new RequestFutureAdapter<ClientResponse, Void>() {
@Override
public void onSuccess(ClientResponse response, RequestFuture<Void> future) {
handleGroupMetadataResponse(response, future);
}
});
}
}
Future#compose(..)方法又两个作用
- 添加FutureRequest的listeners
- 返回一个新的FutureRequest,用新FutureRequest来判断状态
public <S> RequestFuture<S> compose(final RequestFutureAdapter<T, S> adapter) {
final RequestFuture<S> adapted = new RequestFuture<S>(); // 返回新的RequestFuture
addListener(new RequestFutureListener<T>() { // 添加到原先FutureRequest中的listeners中
@Override
public void onSuccess(T value) {
adapter.onSuccess(value, adapted); // 返回response后会调用listeners,从而会设置新的RequestFuture状态,我们就可以根据这个新的RequestFuture来判断response处理状态。
}
@Override
public void onFailure(RuntimeException e) {
adapter.onFailure(e, adapted);
}
});
return adapted;
}
所以将ClientRequest放到map中后,最终我们持有的是compose中新建的FutureRequest,如AbstractCoordinator#ensureCoordinatorReady(..):
public void ensureCoordinatorReady() {
while (coordinatorUnknown()) {
RequestFuture<Void> future = sendGroupCoordinatorRequest();// 最终返回compose返回的future。
client.poll(future); // 在poll中不停的轮训future的状态
if (future.failed()) {
if (future.isRetriable())
client.awaitMetadataUpdate();
else
throw future.exception();
} else if (coordinator != null && client.connectionFailed(coordinator)) {
coordinatorDead();
time.sleep(retryBackoffMs);
}
}
}
public void poll(RequestFuture<?> future) {
while (!future.isDone()) // 轮训future状态,当response做相应处理会调用回调函数,从而设置future相应状态。
poll(Long.MAX_VALUE);
}
总结
kafka客户端中使用了大量的回调函数做请求的处理,理解回调函数很重要,附回调函数链接:
http://www.cnblogs.com/set-cookie/p/8996951.html
kafka中的回调函数的更多相关文章
- PHP中的回调函数和匿名函数
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...
- 理解和使用 JavaScript 中的回调函数
理解和使用 JavaScript 中的回调函数 标签: 回调函数指针js 2014-11-25 01:20 11506人阅读 评论(4) 收藏 举报 分类: JavaScript(4) 目录( ...
- js中的回调函数的理解和使用方法
js中的回调函数的理解和使用方法 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为 ...
- [转]理解与使用Javascript中的回调函数
在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回. 因 ...
- 【JavaScript】理解与使用Javascript中的回调函数
在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回. 因 ...
- C中的回调函数
C语言中应用回调函数的地方非常多,如Nginx中: struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char *(*set)(ngx_c ...
- Java中的回调函数学习
Java中的回调函数学习 博客分类: J2SE JavaJ# 一般来说分为以下几步: 声明回调函数的统一接口interface A,包含方法callback(); 在调用类caller内将该接口设置 ...
- 转: jquery中ajax回调函数使用this
原文地址:jquery中ajax回调函数使用this 写ajax请求的时候success中代码老是不能正常执行,找了半天原因.代码如下 $.ajax({type: 'GET', url: " ...
- 理解javascript中的回调函数(callback)【转】
在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...
随机推荐
- operator的解释
operator是C++的关键字,它和运算符一起使用,表示一个运算符函数,理解时应将operator=整体上视为一个函数名. 这是C++扩展运算符功能的方法,虽然样子古怪,但也可以理解:一方面要使运算 ...
- 对于计算正确率时 logits.argmax(dim=1),torch.eq(pre_label,label)
额 好像是一句非常简单的代码 ,但是作为新手 ,我是完全看不懂哎 前十眼. 首先 这里的logits是一个 (a,b)维的张量.其中a是你的全连接输出维度,b是一个batch中的样本数量. 我们经过 ...
- centos7云服务器安装nginx记录
nginx作为一个web和反向服务器,应用广泛,尤其适合学习c/c++的人进行使用学习,今天就对这个我听了很多的nginx进行了一次安装配置,主要是针对菜鸟教程中的安装引导进行的个人试验.主要的关注点 ...
- 报表工具为什么我推荐用Smartbi,数据分析师和初学者都能灵活运用
在很多人入门数据分析师或者投身大数据行业的时候,肯定会接触到报表工具,很多人这时候就会去使用一些Excel插件的报表工具,但是很多报表工具都是需要下载一系列的软件,配置各种复杂的环境.尤其是一些数据分 ...
- 大数据分析用自助式BI工具就能轻松解决,so easy!
之前老板给了我一个任务,让我赶紧学习一下大数据分析,下个季度就要用. 赶紧看了一下日历,这离下个季度还有不到半个月的时间,而且我还没有数据分析基础,该怎么能在这么短的时间内学会大数据分析呢-- 经过多 ...
- 【C# .Net GC】延迟模式 latencyMode 通过API-GC调优
延迟模式 lowlatency 使用环境:后台工作方式只影响第 2 代中的垃圾回收:第 0 代和第 1 代中的垃圾回收始终是非并发的,因为它们完成的速度很快.GC模式是针对进程配置的,进程运行期间不能 ...
- 【基础知识】CPU指令集
计算机指令就是指挥机器工作的指示和命令,程序就是一系列按一定顺序排列的指令,执行程序的过程就是计算机的工作过程.指令集,就是CPU中用来计算和控制计算机系统的一套指令的集合,而每一种新型的CPU在设计 ...
- SpreadJS + GcExcel 一出,谁与争锋!全栈表格技术轻松应对复杂公式计算场景(一)
设计思路篇 Excel是我们日常办公中最常用的电子表格程序,不仅可满足报表数据的计算需求,还可提供绘图.数据透视分析.BI和Visual Basic for Applications (VBA)宏语言 ...
- [python][nginx][https] Nginx 服务器 SSL 证书安装部署
目录 前言 1 申请证书 2 Nginx 服务器 SSL 证书安装部署 2.1.准备 Nginx 环境 2.2 证书部署 2.3 Nginx 配置 3 最后 参考链接 前言 博主博客中的图片,使用的是 ...
- 分子动力学模拟之SETTLE约束算法
技术背景 在上一篇文章中,我们讨论了在分子动力学里面使用LINCS约束算法及其在具备自动微分能力的Jax框架下的代码实现.约束算法,在分子动力学模拟的过程中时常会使用到,用于固定一些既定的成键关系.例 ...