1 定時任務兩種, 2 消費分組消費從哪裏開始,不消費歷史數據
定時主要是爲了延遲拉消費,最簡單方式 直接批量拉取之後, ack批量提交後, 線程掛起3秒,更好達到效果
if (kafuKfaUtils.insertFlowdata(jsonarray, map)) {
ack.acknowledge();
}
提交之後, 再等待幾秒,以拉取更多數據
long start = System.currentTimeMillis();
System.out.println("線程掛起2秒" + start);
Thread.sleep(2000);//睡眠5秒
long end = System.currentTimeMillis();
System.out.println("線程恢復" + (end - start) / 1000);
第一種監聽的方式定時
springboot1.5.6 + kafka2.1.7 依賴包 包衝突後, 又改回springboot-kafka1.1.1 定時任務不理想,恢復和暫停有問題
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
注入實例 當執行完成後, 取消監聽topic 定時開啓監控,這個在KAFKA之前的版本並不支持 監聽和取消 ,我升級包後纔行
後面主要是邏輯代碼了, 後面貼上
@KafkaListener(id = "flow-task", topics = "topic_collect", group = "flow-topic", containerFactory = "batchFlowFactory") 低版本沒有groupId 兼容問題,暫停會有問題,
@Service
public class KafkaTaskService {
private static final Logger log = LoggerFactory.getLogger(KafkaTaskService.class);
@Autowired
private KafkaListenerEndpointRegistry registry;
/**
* 定時執行
*
* @param recordList
* @param acknowledgment
*/
@KafkaListener(id = "flow-task", topics = "topic_collect", group = "flow-topic", containerFactory = "batchFlowFactory")
public void listenFailEmail(List<ConsumerRecord> recordList, Acknowledgment acknowledgment) {
System.out.println(recordList.size() + "條數據,監聽消費開始");
for (ConsumerRecord record : recordList) {
log.info("fail email-消息:【{}】。", record.value().toString());
}
acknowledgment.acknowledge();
shutdownListener();
System.out.println("********* 監聽消費結束");
}
@Scheduled(cron = "0/20 * * * * ?")
public void startListener() {
log.info("開啓監聽");
MessageListenerContainer container = registry.getListenerContainer("flow-task");
if (!container.isRunning()) {
container.start();
System.out.println("開啓監聽");
}
//恢復
// container.resume();
}
//@Scheduled(cron = "0 08 12 * * ?") //12點08分執行
public void shutdownListener() {
// log.info("關閉監聽");
//暫停
MessageListenerContainer container = registry.getListenerContainer("flow-task");
container.stop();
System.out.println("關閉監聽");
// conta
/**
* kafka監聽工廠
*
* @param configurer
* @return
*/
@Bean("batchFactory")
public ConcurrentKafkaListenerContainerFactory<?, ?> kafkaListenerContainerFactory(
ConcurrentKafkaListenerContainerFactoryConfigurer configurer,
ConsumerFactory consumerFactory) {
ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
//開啓批量消費功能
factory.setBatchListener(true);
//不自動啓動
factory.setAutoStartup(false);
configurer.configure(factory, consumerFactory);
return factory;
}
}
定時任務第二種方式
通過實踐獲取一下結論:
1.topic可以被多個group消費
group之間消費位移互不干擾
2.topic被group消費時,若有多個消費者實例,同一條消息只會被一個消費者處理
3.一個group可以消費多個topic
4.一個消費者group可以拉取多個topic消息
代碼如下 核心代碼 加入新分組可以定時拉任務, 但結果不是自己想要的,一分鐘拉一次,數據處理不完的時候,會怎麼樣
@EnableAsync
@Component
public class GroupQueue {
static boolean RUN = false;
@Async
@Scheduled(cron = "0 * * * * ?")
public void task() {
if (RUN) {
return;
}
RUN = true;
KafkaConsumer consumer = KafkaConsumerFactory.getKafkaConsumer("group2");
consumer.subscribe(Arrays.asList("GroupQueue", "FriendQueue"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
System.out.println("批次:" + UUID.randomUUID().toString());
for (ConsumerRecord<String, String> record : records) {
System.out.printf(record.topic() +
"一條新消息 offset = %d, key = %s, value = %s", record.offset(),
record.key(), record.value());
System.out.println(record.topic() + "partition:" +
record.partition());
// 業務處理 TODO
}
// 同步提交
if (records.count() > 0) {
consumer.commitSync();
System.out.println("批次提交");
}
}
}
public static KafkaConsumer getKafkaConsumer(String group) {
Properties propstask = new Properties();
propstask.put("bootstrap.servers", "107.101.117.118:9092 107.101.117.119:9092 107.101.117.117:9092");
//每個消費者分配獨立的組號
propstask.put("group.id", group);
//如果value合法,則自動提交偏移量
propstask.put("enable.auto.commit", "false");
// 每次拉取5000條
propstask.put("max.poll.records", 10000);
//設置多久一次更新被消費消息的偏移量
propstask.put("auto.commit.interval.ms", "1000");
//設置會話響應的時間,超過這個時間kafka可以選擇放棄消費或者消費下一條消息
propstask.put("session.timeout.ms", "30000");
//自動重置offset
propstask.put("auto.offset.reset", "latest");//latest earliest
propstask.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
propstask.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
return new KafkaConsumer<String, String>(propstask);
}
}
不消費歷史數據,添加分組消費最新數據,放棄消費歷史數據等
設置消費者properties的兩個參數
consumer.group.id
properties.setProperty("auto.offset.reset", "earliest”) // latest
注意:
只要不更改group.id,每次重新消費kafka,都是從上次消費結束的地方繼續開始,不論"auto.offset.reset”屬性設置的是什麼
場景一:Kafka上在實時被灌入數據,但kafka上已經積累了兩天的數據,如何從最新的offset開始消費?
(最新指相對於當前系統時間最新)
1.將group.id換成新的名字(相當於加入新的消費組)
2.網上文章寫還要設置 properties.setProperty("auto.offset.reset", "latest”)
實驗發現即使不設置這個,只要group.id是全新的,就會從最新的的offset開始消費
場景二:kafka在實時在灌入數據,kafka上已經積累了兩天的數據,如何從兩天前最開始的位置消費?
1.將group.id換成新的名字
2.properties.setProperty("auto.offset.reset", "earliest”)
場景三:不更改group.id,只是添加了properties.setProperty("auto.offset.reset", "earliest”),consumer會從兩天前最開始的位置消費嗎?
不會,只要不更改消費組,只會從上次消費結束的地方繼續消費
場景四:不更改group.id,只是添加了properties.setProperty("auto.offset.reset", "latest”),consumer會從距離現在最近的位置消費嗎?
不會,只要不更改消費組,只會從上次消費結束的地方繼續消費
應用:
正式打包上線前應該使用新的group.id,以便於從kafka最新的位置開始消費
只要將group.id換成全新的,不論"auto.offset.reset”是否設置,設置成什麼,都會從最新的位置開始消費