<dependency>
<groupId>org.springframework.kafkagroupId>
<artifactId>spring-kafkaartifactId>
<version>1.0.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.kafkagroupId>
<artifactId>kafka-clientsartifactId>
<version>0.9.0.1version>
dependency>
/**
* kafka生产配置
*/
@Configuration
@EnableKafka
public class KafkaProducerConfig {
@Value("${kafka.producer.servers}")
private String servers;
@Value("${kafka.producer.retries}")
private int retries;
@Value("${kafka.producer.batch.size}")
private int batchSize;
@Value("${kafka.producer.linger}")
private int linger;
@Value("${kafka.producer.buffer.memory}")
private int bufferMemory;
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
props.put(ProducerConfig.RETRIES_CONFIG, retries);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
props.put(ProducerConfig.LINGER_MS_CONFIG, linger);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return props;
}
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<String, String>(producerFactory());
}
}
此处根据需要,对于不同的主题,使用不同的配置参数。之所以没有考虑使用 @KafkaLisener 注解中的group和id,是因为我暂时无法理解其中的group与id与group.id的区别与意义。况且将监听配置写入配置文件也比使用Constant类在语义上更为分明。
/**
* kafka消费者配置
*/
@Configuration
@EnableKafka
public class KafkaConsumerConfig {
@Value("${kafka.consumer.servers}")
private String servers;
@Value("${kafka.consumer.enable.auto.commit}")
private boolean enableAutoCommit;
@Value("${kafka.consumer.session.timeout}")
private String sessionTimeout;
@Value("${kafka.consumer.auto.commit.interval}")
private String autoCommitInterval;
@Value("${kafka.consumer.data.group.id}")
private String dataGroupId;
@Value("${kafka.consumer.model.group.id}")
private String modelGroupId;
@Value("${kafka.consumer.auto.offset.reset}")
private String autoOffsetReset;
@Value("${kafka.consumer.concurrency}")
private int concurrency;
@Value("${kafka.listener.ack-mode}")
private String ack_mode;
@Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerModelContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(ModelConsumerFactory());
//factory.setConcurrency(concurrency);
factory.getContainerProperties().setPollTimeout(1500);
factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.valueOf(ack_mode));
return factory;
}
@Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerDataContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(DataConsumerFactory());
//factory.setConcurrency(concurrency);
factory.getContainerProperties().setPollTimeout(1500);
factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.valueOf(ack_mode));
return factory;
}
public ConsumerFactory<String, String> ModelConsumerFactory() {
return new DefaultKafkaConsumerFactory<>(ModelConsumerConfigs());
}
public ConsumerFactory<String, String> DataConsumerFactory() {
return new DefaultKafkaConsumerFactory<>(DataConsumerConfigs());
}
public Map<String, Object> ModelConsumerConfigs() {
Map<String, Object> propsMap = new HashMap<>();
propsMap.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
propsMap.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, enableAutoCommit);
propsMap.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitInterval);
propsMap.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, sessionTimeout);
propsMap.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propsMap.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propsMap.put(ConsumerConfig.GROUP_ID_CONFIG, modelGroupId);
propsMap.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
return propsMap;
}
public Map<String, Object> DataConsumerConfigs() {
Map<String, Object> propsMap = new HashMap<>();
propsMap.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
propsMap.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, enableAutoCommit);
propsMap.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitInterval);
propsMap.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, sessionTimeout);
propsMap.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propsMap.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propsMap.put(ConsumerConfig.GROUP_ID_CONFIG, dataGroupId);
propsMap.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
return propsMap;
}
}
kafka:
consumer.servers: localhost:9092,localhost:9093
consumer.enable.auto.commit: false
consumer.session.timeout: 6000
consumer.auto.commit.interval: 100
consumer.auto.offset.reset: earliest
consumer.topic: data
consumer.data.group.id: data_container
consumer.model.group.id: model_container_2
consumer.concurrency: 1
listener.ack-mode: MANUAL_IMMEDIATE
producer.servers: localhost:9092
producer.retries: 0
producer.batch.size: 4096
producer.linger: 1
producer.buffer.memory: 40960
配置解释:
新版消费者通过brokers而不是zookeeper是因为对消费端而言zookeeper应该是透明的,消费端需要的是kafka里的数据而zookeeper是kafka集群内部需要的,不应被暴露给外部,当然消费者获取broker信息本质仍是通过zookeeper来获取,只是这一过程被kafka隐藏起来了,从语义上隐藏了zookeeper服务
因为zookeeper要被替代了,kafka团队不想再依赖zk了,目前,Kafka使用ZooKeeper来存储分区和代理的元数据,并选择一个Broker作为Kafka控制器,而希望通过删除对ZooKeeper的依赖,将使Kafka能够以一种更具伸缩性和健壮性的方式管理元数据,实现对更多分区的支持,它还将简化Kafka的部署和配置。但是目前我们还是需要Zookeeper(最新版kafka是2.3.1)
earliest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
latest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
none
topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
- RECORD
每处理一条commit一次- BATCH(默认)
每次poll的时候批量提交一次,频率取决于每次poll的调用频率- TIME
每次间隔ackTime的时间去commit(跟auto commit interval有什么区别呢?)- COUNT
累积达到ackCount次的ack去commit- COUNT_TIME
ackTime或ackCount哪个条件先满足,就commit- MANUAL
listener负责ack,但是背后也是批量上去- MANUAL_IMMEDIATE
listner负责ack,每调用一次,就立即commit
@Autowired
KafkaTemplate kafkaTemplate;
直接调用kafkaTemplate的send方法即可
这里的用法就比较粗糙,应该把topics和id省掉,使用配置中的topics和group.id
@Component
public class ModelConsumer {
@Autowired
KafkaTemplate kafkaTemplate;
@KafkaListener(topics = Constant.MODEL_RECEIVE_TOPIC, id = Constant.MODEL_RECEIVE_GROUP,
containerFactory = "kafkaListenerModelContainerFactory")
public void receiveMessage(ConsumerRecord record, Acknowledgment ack) {
//提交偏移量
ack.acknowledge();
}
}