前言
前文中,已经完成了springboot对kafka的整合,还有一些问题需要解决的就继续记录下。
定制个性化消费者分析
如下,咱们写在配置文件中的消费者的各项配置是针对所有消费者的,那么当咱们想定制个性化消费者的时候,当然就不想用yml中消费者的通用配置了。
怎么解决这个问题呢?大家还记得咱们自己写过的KafkaListenerContainerFactory嘛,当时咱们配置了两个,一个主要是为了批量消费、另一个是为了单条数据消费。这个
KafkaListenerContainerFactory就可以规定个性化的消费者参数,可以对这个类进行扩展达到咱们的目的,怎么玩呢。 其实呀,这还要取决于ConsumerFactory,这个类真正传递过来的实例是DefaultKafkaConsumerFactory
而DefaultKafkaConsumerFactory中有一个属性configs,这个就用来装载消费者的各种配置。
DefaultKafkaConsumerFactory的一个构造方法是,直接传递一个map,那么咱们不用默认的ConsumerFactory,直接自己制造一个DefaultKafkaConsumerFactory来用。看看能否达到目的:
定制个性化消费者实践
创建消费者的各个配置项:
private Map<String, Object> consumerConfig1() {
Map<String, Object> propsMap = new HashMap<>(32);
propsMap.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
propsMap.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "102");
propsMap.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propsMap.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propsMap.put(ConsumerConfig.GROUP_ID_CONFIG, "group-1");
propsMap.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
propsMap.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "20");
propsMap.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"XXXX:9092,xxxx:9092, xxxx:9092");
return propsMap;
}
创建监听工厂:
@Bean("customized1")
@NotNull
public KafkaListenerContainerFactory<?> customizedFactory1() {
ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
// 自定义DefaultKafkaConsumerFactory
DefaultKafkaConsumerFactory<Integer, String> defaultKafkaConsumerFactory = new DefaultKafkaConsumerFactory<Integer, String>(consumerConfig1());
factory.setConsumerFactory(defaultKafkaConsumerFactory);
factory.setConcurrency(5);
factory.getContainerProperties().setPollTimeout(1500);
// 批量拉取数据
factory.setBatchListener(true);
// 设置每个@KafkaListener的线程数
factory.setConcurrency(5);
// 设置手动提交ack,对于要求较为严格的业务较为合适
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
return factory;
}
创建topic:
private static final String TOPIC_THREE = "customizedTopic";
@Bean
public NewTopic initialTopic2() {
log.info("create new topic now");
return TopicBuilder.name(TOPIC_THREE).partitions(6).replicas(2).build();
}
创建监听器:
/**
* @param recordList
* @param acknowledgment 使用自定义的消费者
*/
@KafkaListener(id = "consumer4", topics = "customizedTopic", containerFactory = "customized1",
properties = {
ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG + "=50000" // 如果超过5分钟pull不到数据,那么尝试自行拉取
}
)
public void onMessage4(List<ConsumerRecord<?, ?>> recordList, Acknowledgment acknowledgment) {
logger.info(">>>批量消费一次,records.size()=" + recordList.size());
logger.info("=================================================start");
try {
for (ConsumerRecord<?, ?> record : recordList) {
logger.info("get message from record:{}", record.value());
}
acknowledgment.acknowledge();
logger.info("=================================================end");
} catch (Exception e) {
logger.info("exception occur when consume message:{}", e.getMessage());
acknowledgment.acknowledge();
}
}
重启下项目,配置已经生效:
接下来可以模仿之前的操作,去正常收发消息了。
消费是正常的。
同组不同消费者消费同一topic
为了加快消费速度,可以为一个topic构建多个消费者进行消费。咱们用同组的不同消费者来测试下:
/**
* @param recordList
* @param acknowledgment KafkaListener id :消费者线程的命名规则.
* groupId:指定该消费组的消费组名
* topics:指定要监听哪些topic 这个是从所有的分区取数据吗?
*/
@KafkaListener(id = "consumer2", groupId = "felix-group", topics = "testtopic1", containerFactory = "batch",
properties = {
ConsumerConfig.MAX_POLL_RECORDS_CONFIG + "=10",// 每次最多取10个进行消费
ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG + "=50000" // 如果超过5分钟pull不到数据,那么尝试自行拉取
}
)
public void onMessage3(List<ConsumerRecord<?, ?>> recordList, Acknowledgment acknowledgment) {
logger.info(">>>批量消费一次,records.size()=" + recordList.size());
logger.info("===================================================start");
try {
for (ConsumerRecord<?, ?> record : recordList) {
Object value = record.value();
logger.info("get message from record:{}", value);
copyOnWriteArrayList1.add(record.value().toString());
}
acknowledgment.acknowledge();
logger.info("====================================================end");
} catch (Exception e) {
logger.info("exception occur when consume message:{}", e.getMessage());
acknowledgment.acknowledge();
}
}
/**
* @param recordList
* @param acknowledgment 同一组内的不同消费者的消费情况咱们来观察一下 针对的是testtopic1,两个消费者都属于felix-group
* 这里我想说的是,为了保证数据可以不重复消费,咱们最好还是对消息做幂等的操作吧
*/
@KafkaListener(id = "consumer23", groupId = "felix-group", topics = "testtopic1", containerFactory = "batch",
properties = {
ConsumerConfig.MAX_POLL_RECORDS_CONFIG + "=10",// 每次最多取10个进行消费
ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG + "=50000" // 如果超过5分钟pull不到数据,那么尝试自行拉取
}
)
public void onMessage(List<ConsumerRecord<?, ?>> recordList, Acknowledgment acknowledgment) {
logger.info(">>>批量消费一次,同一组内的第二个消费者!records.size()=" + recordList.size());
logger.info("=================================================start");
try {
for (ConsumerRecord<?, ?> record : recordList) {
Object value = record.value();
logger.info("get message from record:{}", record.value());
copyOnWriteArrayList2.add(value.toString());
}
acknowledgment.acknowledge();
logger.info("==================================================end");
} catch (Exception e) {
logger.info("exception occur when consume message:{}", e.getMessage());
acknowledgment.acknowledge();
}
}
来简单测试下:
可以的 ,没什么问题。不同组的不同消费者对同一topic的消费还存在些问题。
本篇暂时到此。