前言
本文主要结合源码对 kafka consumer 拉取数据的消费过程进行分析。
数据消费
数据消费的入口存在于 ListenerConsumer
类的 pollAndInvoke
方法中,由 invokeListener(records)
进行执行。
invokeListener(records)
实现为:
private void invokeListener(final ConsumerRecords<K, V> records) {
if (this.isBatchListener) {
// 批量消费
invokeBatchListener(records);
}
else {
// 单条消费
invokeRecordListener(records);
}
}
这里分析单条数据的消费情况:
private void invokeRecordListener(final ConsumerRecords<K, V> records) {
if (this.transactionTemplate != null) {
// 开启事务消费数据
invokeRecordListenerInTx(records);
}
else {
// 正常的消费数据
doInvokeWithRecords(records);
}
}
private void doInvokeWithRecords(final ConsumerRecords<K, V> records) {
Iterator<ConsumerRecord<K, V>> iterator = records.iterator();
// 遍历数据,执行消费过程
while (iterator.hasNext()) {
final ConsumerRecord<K, V> record = iterator.next();
this.logger.trace(() -> "Processing " + record);
// 执行消费
doInvokeRecordListener(record, null, iterator);
if (this.nackSleep >= 0) {
handleNack(records, record);
break;
}
}
}
private RuntimeException doInvokeRecordListener(final ConsumerRecord<K, V> record,
@SuppressWarnings(RAW_TYPES) Producer producer,
Iterator<ConsumerRecord<K, V>> iterator) {
Object sample = startMicrometerSample();
try {
// 数据消费
invokeOnMessage(record, producer);
successTimer(sample);
}
catch (RuntimeException e) {
failureTimer(sample);
if (this.containerProperties.isAckOnError() && !this.autoCommit && producer == null) {
// 如果设置了在出现异常时的数据提交且为手动提交形式,执行offset的提交
ackCurrent(record);
}
if (this.errorHandler == null) {
throw e;
}
try {
// 执行 error handler
invokeErrorHandler(record, producer, iterator, e);
}
catch (RuntimeException ee) {
this.logger.error(ee, "Error handler threw an exception");
return ee;
}
catch (Error er) { // NOSONAR
this.logger.error(er, "Error handler threw an error");
throw er;
}
}
return null;
}
在 RecordMessagingMessageListenerAdapter 类中的 onMessage 方法执行:
public void onMessage(ConsumerRecord<K, V> record, Acknowledgment acknowledgment, Consumer<?, ?> consumer) {
Message<?> message;
if (isConversionNeeded()) {
message = toMessagingMessage(record, acknowledgment, consumer);
}
else {
message = NULL_MESSAGE;
}
if (logger.isDebugEnabled()) {
logger.debug("Processing [" + message + "]");
}
try {
// 携带参数,执行消费
Object result = invokeHandler(record, acknowledgment, message, consumer);
if (result != null) {
handleResult(result, record, message);
}
}
catch (ListenerExecutionFailedException e) { // NOSONAR ex flow control
if (this.errorHandler != null) {
try {
if (message.equals(NULL_MESSAGE)) {
message = new GenericMessage<>(record);
}
Object result = this.errorHandler.handleError(message, e, consumer);
if (result != null) {
handleResult(result, record, message);
}
}
catch (Exception ex) {
throw new ListenerExecutionFailedException(createMessagingErrorMessage(// NOSONAR stack trace loss
"Listener error handler threw an exception for the incoming message",
message.getPayload()), ex);
}
}
else {
throw e;
}
}
}
在 InvocableHandlerMethod 中 invoke 方法会匹配方法参数,将必要参数传入method并执行:
public Object invoke(Message<?> message, Object... providedArgs) throws Exception {
// 匹配方法参数,返回需要调用方法的实际参数,如果方法参数中有 acknowledge 参数,则会校验是否为手动提交方式
Object[] args = getMethodArgumentValues(message, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 执行消费方法,此步实际执行为手动定义的消费方法
return doInvoke(args);
}
总结
整个数据消费的过程相对其余数据拉取、offset提交等比较简单明了。