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”是否设置,设置成什么,都会从最新的位置开始消费