消费者和消费组
拥有相同group.id的消费者线程为同一组消费组
消费者数量和分区数量一致时为p2p模式;消费者数量少于分区数量为发布订阅模式
订阅主题与分
subscribe()方法为订阅主题,有4个重载的方法
public void subscribe(Collection<String> topics, ConsumerRelanceListener listener)
public void subscribe(Collection<String> topics)
public void subscribe(Pattern pattern, ConsumerRelanceListener listener)
public void subscribe(Pattern pattern)
Pattern.compile("topic-*")
指定分区来消费
public void assign(Collection<TopicPartition> partitions)
使用KafkaConsumer的partitionsFor()方法来获取主题的元数据
public List<PartitionInfo> partitionsFor(String topic)
PartitionInfo的实例属性
String topic
int partiiton
Node leader
Node[] replicas
Node[] inSyncReplicas
Node[] offlineReplicas
消息消费
Kafka是基于拉模式的,一般来说是拉一片处理一批在拉下一批
拉取的方法为
public ConsumerRecords<K,V> poll(final Duration timeout)
public ConsumerRecords<K,V> poll(final long timeout)
//在没有数据的时候,poll方法将处于阻塞状态,而timeout则是决定会阻塞多长时间
ConsumerRecords实例属性
String topic
int partition
long offset
long timestamp
TimestampType timestampType
int serializedKeySize
int serializedValueSize
Headers headers
K key
V value
Long checksum
ConsumerRecords实现了Iterable接口,可以直接使用for each循环来获取,同时iterator()方法也返回一个Iterable<ConsumerRecord<K,V>>
获取指定topic和partition下的数据
public List<ConsumerRecord<K,V>> records(TopicPartition partition)
public Iterable<ConsumerRecord<K,V>> records(String topic)
这里是两个方法的源码,可以看下
/**
* Get just the records for the given partition
*
* @param partition The partition to get records for
*/
public List<ConsumerRecord<K, V>> records(TopicPartition partition) {
List<ConsumerRecord<K, V>> recs = this.records.get(partition);
if (recs == null)
return Collections.emptyList();
else
return Collections.unmodifiableList(recs);
}
/**
* Get just the records for the given topic
*/
public Iterable<ConsumerRecord<K, V>> records(String topic) {
if (topic == null)
throw new IllegalArgumentException("Topic must be non-null.");
List<List<ConsumerRecord<K, V>>> recs = new ArrayList<>();
for (Map.Entry<TopicPartition, List<ConsumerRecord<K, V>>> entry : records.entrySet()) {
if (entry.getKey().topic().equals(topic))
recs.add(entry.getValue());
}
return new ConcatenatedIterable<>(recs);
}
//从源码中可以看到其实就是讲ConsumerRecords类中的records属性中的具体的partition中的数据list拿出来了
获取这个ConsumerRecords下的topic和parition列表
public List<TopicPartition> partitions()
KafkaConsumer提供了postition(TopicPartition)和committed(TopicPartition)来获取postion和comittedoffset的位置
提交
如果不使用自动提交,需要设置参数
enable.auto.commit=false
同步提交(手动)
public void commitSync()
public void commitSync(final Map<TopicPartition,OffsetAndMetadata> offset)
异步提交(手动)
public void commitAsync(final Map<TopicPartition,OffsetAndMetadata> offset, OffsetCommitCallback callback)
// OffsetCommitCallback接口有一个onComplete的方法
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception)
// 两个参数互斥
控制和关闭消费
pasuse和resume两个方法
指定位移消费
auto.offset.reset来控制消费的起始点:earliest,latest和none
默认为earliest,从头开始消费
以上是自动,下面是手动
public void seek(TopicPartition partition, long offset)
//在seek方法执行前要先执行一次poll
在执行seek前还可以先获取这批数据的topic和partition情况
public Set<TopicPartition> assignment()
一般offset存储在外部db当中,可能要先去查一下offset
while(assignment == 0) {
consumer.poll(10000)
assignment = consumer.assignment()
}
获取指定分区的最后offset
public Map<TopicPartiion,Long> endOffsets(Collection<TopicPartition> partitions)
根据时间来获取offset
//该方法用于查询需要的offset
public Map<TopicPartition, OffsetAndTimestamp> offsetForTimes(Map<TopicPartition,Long> timestampToSearch)
再平衡
主要是subscribe中的ConsumerRelanceListener接口的重写
//这个方法在停止读数据之后和再平衡之前执行
public void onPartitionRevoked(Collection<TopicPartition> partitions)
//这个方法在再平衡之后和开始读数据之前执行
public void onPartitionAssigned(Collection<TopicPartition> partitions)
拦截器
实现一个拦截器接口后,重写onConsume和onCommit方法
前者在poll返回前执行,后者在提交offset后执行