@Author : Spinach | GHB
@Link : http://blog.csdn.net/bocai8058
三種情況的消費者不同使用方式下,消費者提交offset的情況進行了歸總和說明:
從開頭處獲取數據
配置設置
設置auto.offset.reset爲earliest或latest
默認是latest(消費者從最新的記錄開始讀取數據);
earliest(消費者從起始位置讀取partition的記錄)
Kafka-SparkStreaming設置
val conf = new SparkConf().setMaster("local[4]").setAppName("TagGen");
val sc = new StreamingContext(conf, Seconds(15)); //5 minter
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "10.1.3.5:6667",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "test-consumer-group",
"auto.offset.reset" -> "earliest", //從分區末尾獲取數據,還可設置爲earliest(從分區頭獲取數據)
"enable.auto.commit" -> (false: java.lang.Boolean))
val topics = Array("TagGen")
// 解決數據積壓問題
val stream = KafkaUtils.createDirectStream[String, String](
sc,
PreferConsistent,
Subscribe[String, String](topics, kafkaParams)
)
val sources = stream.map(e => e.value().toString) //e.value() 是kafka消息內容,e.key爲空值
代碼實現
/**
* @author yujie.wang
* kafka消費者示例,包含隨機位置消費和最多一次消費方式
* 消費者提交消費數據offset 分爲自動提交和手動控制提交
*
* 這份代碼示例中包含了 多種從kafka的任意位置獲取數據的方式
*/
public class Consumer_Sample {
//kafka集羣機器
private static final String KAFKA_HOSTS = "10.4.30.151:9092,10.4.30.151:9093,10.4.30.151:9094";
//topic名稱
private static final String TOPIC = "my-replicated-topic_2";
public static void main(String[] args) {
// TODO Auto-generated method stub
Consumer_Sample consumer = new Consumer_Sample();
//從指定的分區 開始位置seekToBeginning 或者任意位置seek消費數據
consumer.consumerAssin("true", TOPIC);
System.out.println("consumer end");
}
/**
* 通過assign分配的分區,消費者發生故障 Server端不會觸發分區重平衡(即使該消費者共享某個已有的groupId),每個消費者都是獨立工作的
* 爲了避免offset提交衝突,需要確保每個消費者都有唯一的groupId
* 從指定的分區的開頭開始消費數據
* @param isAutoCommitBool true 開啓自動提交offset;false 不開啓
* @param topic
*/
public void consumerAssin(String isAutoCommitBool,String topic){
Properties props = new Properties();
//配置kafka集羣機器
props.put("bootstrap.servers", KAFKA_HOSTS);
//消費者分組
props.put("group.id", "yujie35");
//這裏設置 消費者自動提交已消費消息的offset
props.put("enable.auto.commit", isAutoCommitBool);
// 設置自動提交的時間間隔爲1000毫秒
props.put("auto.commit.interval.ms", "1000");
// 設置每次poll的最大數據個數
props.put("max.poll.records", 5);
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//獲得topic的所有分區
List<PartitionInfo> parList = consumer.partitionsFor(topic);
//打印出分區信息
printPartition(parList);
List<TopicPartition> list = new ArrayList<TopicPartition>();
for(PartitionInfo par : parList){
TopicPartition partition = new TopicPartition(topic, par.partition());
list.add(partition);
}
//消費者指定要消費的分區,指定分區之後消費者崩潰之後 不會引發分區reblance
consumer.assign(list);
//從list中所有分區的開頭開始消費數據,這個操作不改變已提交的消費數據的offset
// consumer.seekToBeginning(list);
/* for(TopicPartition tpar:list ){
//consumer.seek(tpar, position);
} */
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
System.out.println("topic: "+topic + " pool return records size: "+ records.count());
for (ConsumerRecord<String, String> record : records){
System.out.println(record.toString());
//手動提交已消費數據的offset
if("false".equalsIgnoreCase(isAutoCommitBool)){
consumer.commitSync();
}
}
}
}
}
從當前最新獲取數據
配置設置
設置auto.offset.reset爲earliest或latest
默認是latest(消費者從最新的記錄開始讀取數據);
earliest(消費者從起始位置讀取partition的記錄)
Kafka-SparkStreaming設置
val conf = new SparkConf().setMaster("local[4]").setAppName("TagGen");
val sc = new StreamingContext(conf, Seconds(15)); //5 minter
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "10.1.3.5:6667",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "test-consumer-group",
"auto.offset.reset" -> "earliest", //從分區末尾獲取數據,還可設置爲earliest(從分區頭獲取數據)
"enable.auto.commit" -> (false: java.lang.Boolean))
val topics = Array("TagGen")
// 解決數據積壓問題
val stream = KafkaUtils.createDirectStream[String, String](
sc,
PreferConsistent,
Subscribe[String, String](topics, kafkaParams)
)
val sources = stream.map(e => e.value().toString) //e.value() 是kafka消息內容,e.key爲空值
代碼實現
/**
* @author yujie.wang
* kafka消費者示例,包含隨機位置消費和最多一次消費方式
* 消費者提交消費數據offset 分爲自動提交和手動控制提交
*
* 這份代碼示例中包含了 多種從kafka的任意位置獲取數據的方式
*/
public class Consumer_Sample {
//kafka集羣機器
private static final String KAFKA_HOSTS = "10.4.30.151:9092,10.4.30.151:9093,10.4.30.151:9094";
//topic名稱
private static final String TOPIC = "my-replicated-topic_2";
public static void main(String[] args) {
// TODO Auto-generated method stub
Consumer_Sample consumer = new Consumer_Sample();
//從分區的末尾 或者已存在groupid的請情況下從未消費位置開始消費數據
consumer.consumerSubscribe("true", TOPIC);
//通過配置屬性auto.offset.reset 來設置消費者從分區開頭或者末尾進行消費,但是需要使用一定條件的group Id
consumer.consumerAutoOffsetReset("true", TOPIC);
System.out.println("consumer end");
}
/**
* 直接通過訂閱一個指定分區來消費數據
* (1)如果該groupId消費者分組下 有消費者提交過offset,則從 當前提交的offset位置開始消費數據
* (2)如果該groupId消費者分組下 沒有有消費者提交過offset,則從 當前log添加的最後位置(也就是數據的末尾)開始消費數據
* @param isAutoCommitBool
* @param topic
*/
public void consumerSubscribe(final String isAutoCommitBool, final String topic){
Properties props = new Properties();
//配置kafka集羣機器
props.put("bootstrap.servers", KAFKA_HOSTS);
//消費者分組
props.put("group.id", "yujie37");
//這裏設置 消費者自動提交已消費消息的offset
props.put("enable.auto.commit", isAutoCommitBool);
// 設置自動提交的時間間隔爲1000毫秒
props.put("auto.commit.interval.ms", "1000");
// 設置每次poll的最大數據個數
props.put("max.poll.records", 5);
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
final KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//訂閱topic
consumer.subscribe(Arrays.asList(topic));
List<PartitionInfo> parList = consumer.partitionsFor(topic);
//打印出分區信息
printPartition(parList);
//消費數據
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
System.out.println("topic: "+topic + " pool return records size: "+ records.count());
for (ConsumerRecord<String, String> record : records){
System.out.println(record.toString());
//手動提交已消費數據的offset
if("false".equalsIgnoreCase(isAutoCommitBool)){
consumer.commitSync();
}
}
}
}
/**
*
* @param isAutoCommitBool true 開啓自動提交offset;false 不開啓
* @param topic
* 如果groupId之前存在 , 則從之前提交的最後消費數據的offset處繼續開始消費數據
* 如果groupId之前不存在,則從當前分區的最後位置開始消費
*
* 注意如果enable.auto.commit 設置爲false,如果消費完數據沒有提交已消費數據的offset,
* 則會出現重複消費數據的情況
*/
public void consumerAutoOffsetReset(final String isAutoCommitBool, final String topic){
Properties props = new Properties();
//配置kafka集羣機器
props.put("bootstrap.servers", KAFKA_HOSTS);
//消費者分組
props.put("group.id", "yujie32");
//這裏設置 消費者自動提交已消費消息的offset
props.put("enable.auto.commit", isAutoCommitBool);
// 設置自動提交的時間間隔爲1000毫秒
props.put("auto.commit.interval.ms", "1000");
// 設置每次poll的最大數據個數
props.put("max.poll.records", 5);
//設置使用最開始的offset偏移量爲該group.id的最早。如果不設置,則會是latest即該topic最新一個消息的offset
//如果採用latest,消費者只能得道其啓動後,生產者生產的消息
//一般配置earliest 或者latest 值
props.put("auto.offset.reset", "latest");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
final KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//訂閱topic,並實現ConsumerRebalanceListener
consumer.subscribe(Arrays.asList(topic));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
System.out.println("topic: "+topic + "pool return records size: "+ records.count());
for (ConsumerRecord<String, String> record : records){
System.out.println(record.toString());
//手動提交已消費數據的offset
if("false".equalsIgnoreCase(isAutoCommitBool)){
consumer.commitSync();
}
}
}
}
public void printPartition(List<PartitionInfo> parList){
for(PartitionInfo p : parList){
System.out.println(p.toString());
}
}
/**
* 單獨處理每個分區中的數據,處理完了之後異步提交offset,注意提交的offset是程序將要讀取的下一條消息的offset
* @param consumer
*/
public void handlerData(KafkaConsumer<String, String> consumer){
boolean running = true;
try {
while(running) {
ConsumerRecords<String, String> records = consumer.poll(Long.MAX_VALUE);
for (TopicPartition partition : records.partitions()) {
List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);
for (ConsumerRecord<String, String> record : partitionRecords) {
System.out.println(record.offset() + ": " + record.value());
}
long lastOffset = partitionRecords.get(partitionRecords.size() - 1).offset();
//注意提交的offset是程序將要讀取的下一條消息的offset
consumer.commitSync(Collections.singletonMap(partition, new OffsetAndMetadata(lastOffset + 1)));
}
}
} finally {
consumer.close();
}
}
}
從上次斷開處獲取數據
代碼實現
/**
* @author yujie.wang
* kafka消費者示例,包含隨機位置消費和最多一次消費方式
* 消費者提交消費數據offset 分爲自動提交和手動控制提交
*
* 這份代碼示例中包含了 多種從kafka的任意位置獲取數據的方式
*/
public class Consumer_Sample {
//kafka集羣機器
private static final String KAFKA_HOSTS = "10.4.30.151:9092,10.4.30.151:9093,10.4.30.151:9094";
//topic名稱
private static final String TOPIC = "my-replicated-topic_2";
public static void main(String[] args) {
// TODO Auto-generated method stub
Consumer_Sample consumer = new Consumer_Sample();
//從分區的末尾 或者已存在groupid的請情況下從未消費位置開始消費數據
consumer.consumerSubscribe("true", TOPIC);
System.out.println("consumer end");
}
/**
* 直接通過訂閱一個指定分區來消費數據
* (1)如果該groupId消費者分組下 有消費者提交過offset,則從 當前提交的offset位置開始消費數據
* (2)如果該groupId消費者分組下 沒有有消費者提交過offset,則從 當前log添加的最後位置(也就是數據的末尾)開始消費數據
* @param isAutoCommitBool
* @param topic
*/
public void consumerSubscribe(final String isAutoCommitBool, final String topic){
Properties props = new Properties();
//配置kafka集羣機器
props.put("bootstrap.servers", KAFKA_HOSTS);
//消費者分組
props.put("group.id", "yujie37");
//這裏設置 消費者自動提交已消費消息的offset
props.put("enable.auto.commit", isAutoCommitBool);
// 設置自動提交的時間間隔爲1000毫秒
props.put("auto.commit.interval.ms", "1000");
// 設置每次poll的最大數據個數
props.put("max.poll.records", 5);
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
final KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//訂閱topic
consumer.subscribe(Arrays.asList(topic));
List<PartitionInfo> parList = consumer.partitionsFor(topic);
//打印出分區信息
printPartition(parList);
//消費數據
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
System.out.println("topic: "+topic + " pool return records size: "+ records.count());
for (ConsumerRecord<String, String> record : records){
System.out.println(record.toString());
//手動提交已消費數據的offset
if("false".equalsIgnoreCase(isAutoCommitBool)){
consumer.commitSync();
}
}
}
}
}
從指定offset處獲取數據
代碼實現
/**
* @author yujie.wang
* kafka消費者示例,包含隨機位置消費和最多一次消費方式
* 消費者提交消費數據offset 分爲自動提交和手動控制提交
*
* 這份代碼示例中包含了 多種從kafka的任意位置獲取數據的方式
*/
public class Consumer_Sample {
//kafka集羣機器
private static final String KAFKA_HOSTS = "10.4.30.151:9092,10.4.30.151:9093,10.4.30.151:9094";
//topic名稱
private static final String TOPIC = "my-replicated-topic_2";
public static void main(String[] args) {
// TODO Auto-generated method stub
Consumer_Sample consumer = new Consumer_Sample();
//從指定的分區 開始位置seekToBeginning 或者任意位置seek消費數據
consumer.consumerAssin("true", TOPIC);
System.out.println("consumer end");
}
/**
* 通過assign分配的分區,消費者發生故障 Server端不會觸發分區重平衡(即使該消費者共享某個已有的groupId),每個消費者都是獨立工作的
* 爲了避免offset提交衝突,需要確保每個消費者都有唯一的groupId
* 從指定的分區的開頭開始消費數據
* @param isAutoCommitBool true 開啓自動提交offset;false 不開啓
* @param topic
*/
public void consumerAssin(String isAutoCommitBool,String topic){
Properties props = new Properties();
//配置kafka集羣機器
props.put("bootstrap.servers", KAFKA_HOSTS);
//消費者分組
props.put("group.id", "yujie35");
//這裏設置 消費者自動提交已消費消息的offset
props.put("enable.auto.commit", isAutoCommitBool);
// 設置自動提交的時間間隔爲1000毫秒
props.put("auto.commit.interval.ms", "1000");
// 設置每次poll的最大數據個數
props.put("max.poll.records", 5);
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//獲得topic的所有分區
List<PartitionInfo> parList = consumer.partitionsFor(topic);
//打印出分區信息
printPartition(parList);
List<TopicPartition> list = new ArrayList<TopicPartition>();
for(PartitionInfo par : parList){
TopicPartition partition = new TopicPartition(topic, par.partition());
list.add(partition);
}
//消費者指定要消費的分區,指定分區之後消費者崩潰之後 不會引發分區reblance
consumer.assign(list);
//從list中所有分區的開頭開始消費數據,這個操作不改變已提交的消費數據的offset
// consumer.seekToBeginning(list);
/* for(TopicPartition tpar:list ){
//consumer.seek(tpar, position);
} */
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
System.out.println("topic: "+topic + " pool return records size: "+ records.count());
for (ConsumerRecord<String, String> record : records){
System.out.println(record.toString());
//手動提交已消費數據的offset
if("false".equalsIgnoreCase(isAutoCommitBool)){
consumer.commitSync();
}
}
}
}
}
從指定時刻獲取數據
/**
* @author yujie.wang
* kafka消費者示例,包含隨機位置消費和最多一次消費方式
* 消費者提交消費數據offset 分爲自動提交和手動控制提交
*
* 這份代碼示例中包含了 多種從kafka的任意位置獲取數據的方式
*/
public class Consumer_Sample {
//kafka集羣機器
private static final String KAFKA_HOSTS = "10.4.30.151:9092,10.4.30.151:9093,10.4.30.151:9094";
//topic名稱
private static final String TOPIC = "my-replicated-topic_2";
public static void main(String[] args) {
// TODO Auto-generated method stub
Consumer_Sample consumer = new Consumer_Sample();
// 通過實現ConsumerRebalanceListener接口 進而時間任意位置的消費
consumer.consumerSubscribeImplListener("true", TOPIC);
System.out.println("consumer end");
}
/**
*
* @param isAutoCommitBool true 開啓自動提交offset;false 不開啓
* @param topic
* (1)如果該groupId消費者分組下 有消費者提交過offset,則從 當前提交的offset位置開始消費數據
* (2)如果該groupId消費者分組下 沒有有消費者提交過offset,則從 當前log添加的最後位置(也就是數據的末尾)開始消費數據
*
* 注意如果enable.auto.commit 設置爲false,如果消費完數據沒有提交已消費數據的offset,
* 則會出現重複消費數據的情況
*
* 通過實現ConsumerRebalanceListener接口中的onPartitionsAssigned方法,並在其中調用消費者的seek或者seekToBeginning
* 方法定位分區的任意位置或者開頭位置
*/
public void consumerSubscribeImplListener(final String isAutoCommitBool, final String topic){
Properties props = new Properties();
//配置kafka集羣機器
props.put("bootstrap.servers", KAFKA_HOSTS);
//消費者分組
props.put("group.id", "yujie26");
//這裏設置 消費者自動提交已消費消息的offset
props.put("enable.auto.commit", isAutoCommitBool);
// 設置自動提交的時間間隔爲1000毫秒
props.put("auto.commit.interval.ms", "1000");
// 設置每次poll的最大數據個數
props.put("max.poll.records", 5);
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
final KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//訂閱topic,並實現ConsumerRebalanceListener
consumer.subscribe(Arrays.asList(topic), new ConsumerRebalanceListener(){
@Override
public void onPartitionsRevoked(//分區撤銷時,消費者可以向該分區提交自己當前的offset
Collection<TopicPartition> collection) {
// TODO Auto-generated method stub
if("false".equalsIgnoreCase(isAutoCommitBool)){
//consumer.commitSync();
}
}
@Override
public void onPartitionsAssigned(//當分區分配給消費者時,消費者可以通過該方法重新定位需要消費的數據位置
Collection<TopicPartition> collection) {
// TODO Auto-generated method stub
//將消費者定位到各個分區的開始位置進行消費
/* consumer.seekToBeginning(collection);
System.out.println("seek beg");*/
Iterator it = collection.iterator();
while(it.hasNext()){
//將消費者定位到指定分區的指定位置7進行消費
consumer.seek((TopicPartition)it.next(), 7);
}
}
});
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
System.out.println("topic: "+topic + "pool return records size: "+ records.count());
for (ConsumerRecord<String, String> record : records){
System.out.println(record.toString());
//手動提交已消費數據的offset
if("false".equalsIgnoreCase(isAutoCommitBool)){
consumer.commitSync();
}
}
}
}
}
引用:https://blog.csdn.net/u011784767/article/details/78663168