添加依赖:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
- Spring Boot 1.5用户应使用1.3.x(默认情况下,Boot依赖管理将使用1.1.x,因此应予以覆盖)。
- Spring Boot 2.1用户应使用2.2.x(引导依赖项管理将使用正确的版本)。
- Spring Boot 2.2用户应使用2.3.x(引导依赖项管理将使用正确的版本)。
Produce
kafkaTemplate
包装了生产者,提供更简单的消息发送方法,有以下几种包装的方法:
ListenableFuture<SendResult<K, V>> sendDefault(V data);
ListenableFuture<SendResult<K, V>> sendDefault(K key, V data);
//通过指定相同key来将同一个流程下的消息hash到同一个partition上,使其消费时能保证消息消费顺序
ListenableFuture<SendResult<K, V>> sendDefault(Integer partition, K key, V data);
//此api中的timestamp参数,在topic 配置 message.timestamp.type 为 CreateTime或未指定时生效,
//如果为 LogAppendTime 则使用message 发送到broker时的本地时间
ListenableFuture<SendResult<K, V>> sendDefault(Integer partition, Long timestamp, K key, V data);
ListenableFuture<SendResult<K, V>> send(String topic, V data);
ListenableFuture<SendResult<K, V>> send(String topic, K key, V data);
ListenableFuture<SendResult<K, V>> send(String topic, Integer partition, K key, V data);
ListenableFuture<SendResult<K, V>> send(String topic, Integer partition, Long timestamp, K key, V data);
ListenableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record);
ListenableFuture<SendResult<K, V>> send(Message<?> message);
Map<MetricName, ? extends Metric> metrics();
List<PartitionInfo> partitionsFor(String topic);
<T> T execute(ProducerCallback<K, V, T> callback);
// Flush the producer.
void flush();
interface ProducerCallback<K, V, T> {
T doInKafka(Producer<K, V> producer);
}
config
使用java配置
@Configuration
public class KafkaProduceConfig {
@Bean
KafkaTemplate<Integer, String> kafkaTemplate() {
KafkaTemplate<Integer,String> kafkaTemplate = new KafkaTemplate<>(producerFactory());
kafkaTemplate.setDefaultTopic("test");//调用sendDefault()方法时,默认发送到该主题
kafkaTemplate.setProducerListener(producerListener());
return kafkaTemplate;
}
@Bean
public ProducerFactory<Integer, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
@Bean
public ProducerListener<Integer, String> producerListener () {
ProducerListener<Integer, String> producerListener = new ProducerListener<Integer, String>() {
@Override
public void onSuccess(ProducerRecord producerRecord, RecordMetadata recordMetadata) {
System.out.println("消息发送成功 + 1");
}
@Override
public void onSuccess(String topic, Integer partition, Integer key, String value, RecordMetadata recordMetadata) {
System.out.println("消息发送成功 + 2");
}
@Override
public void onError(ProducerRecord producerRecord, Exception exception) {
System.out.println("消息发送失败 + 1");
}
@Override
public void onError(String topic, Integer partition, Integer key, String value, Exception exception) {
System.out.println("消息发送失败 + 2");
}
};
return producerListener;
}
@Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "39.108.107.152:9092,39.108.107.152:9091,39.108.107.152:9093");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.ACKS_CONFIG, "0");
// See https://kafka.apache.org/documentation/#producerconfigs for more properties
return props;
}
}
sendMessage
@Component
public class SimpleProducer {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleProducer.class);
@Autowired
KafkaTemplate<Integer, String> kafkaTemplate;
public void simpleProduce(String topic, String data) throws ExecutionException, InterruptedException {
ListenableFuture<SendResult<Integer, String>> future = kafkaTemplate.send(topic, 123, data);
future.addCallback(new ListenableFutureCallback<SendResult<Integer, String>>() {
@Override
public void onFailure(Throwable throwable) {
LOGGER.error("发送失败");
}
@Override
public void onSuccess(SendResult<Integer, String> integerStringSendResult) {
LOGGER.info("发送成功");
}
});
SendResult<Integer, String> result = future.get();
LOGGER.info(result.toString());
LOGGER.info("方法执行结束");
}
public void defaultProduce(Integer partition, String data) {
ListenableFuture<SendResult<Integer, String>> result = kafkaTemplate.sendDefault(partition, data);
// ...
}
public void recordProduce(ProducerRecord<Integer, String> record) throws ExecutionException, InterruptedException {
ListenableFuture<SendResult<Integer, String>> result = kafkaTemplate.send(record);
// ...
}
}
Transactions
spring for kafka 中支持以下方式对kafka事务管理
- KafkaTransactionManager: 与常规的Spring事务支持(@ Transactional,TransactionTemplate等)一起使用。
- Transactional KafkaMessageListenerContainer
- Local transactions with KafkaTemplate
Consummer
message listener
spring kafka提供了八个支持的消息侦听器接口。以下清单显示了这些接口:
onMessage方法的参数即为我们使用@KafkaListener注解的监听方法的入参。会根据我们的入参
//使用自动提交或容器管理的提交方法之一时,可使用此接口处理ConsumerRecord从Kafka使用者poll()操作接收的单个实例。
public interface MessageListener<K, V> {
void onMessage(ConsumerRecord<K, V> data);
}
//使用手动提交方法之一时,可使用此接口来处理ConsumerRecord从Kafka使用者poll()操作接收的单个实例。
public interface AcknowledgingMessageListener<K, V> {
void onMessage(ConsumerRecord<K, V> data, Acknowledgment acknowledgment);
}
//使用自动提交或容器管理的提交方法之一时,可使用此接口处理ConsumerRecord从Kafka使用者poll()操作接收的单个实例。提供对Consumer对象的访问
public interface ConsumerAwareMessageListener<K, V> extends MessageListener<K, V> {
void onMessage(ConsumerRecord<K, V> data, Consumer<?, ?> consumer);
}
//使用手动提交方法之一时,可使用此接口来处理ConsumerRecord从Kafka使用者poll()操作接收的单个实例。提供对Consumer对象的访问。
public interface AcknowledgingConsumerAwareMessageListener<K, V> extends MessageListener<K, V> {
void onMessage(ConsumerRecord<K, V> data, Acknowledgment acknowledgment, Consumer<?, ?> consumer);
}
//使用自动提交或容器管理的提交方法之一时,可使用此接口处理从Kafka消费者poll()操作接收的所有ConsumerRecord实例。
//使用此接口时,不支持AckMode.RECORD,因为已为侦听器提供了完整的批处理。
public interface BatchMessageListener<K, V> {
void onMessage(List<ConsumerRecord<K, V>> data);
}
//使用一种手动提交方法时,可使用此接口处理从Kafka消费者poll()操作接收的所有ConsumerRecord实例。
public interface BatchAcknowledgingMessageListener<K, V> {
void onMessage(List<ConsumerRecord<K, V>> data, Acknowledgment acknowledgment);
}
//使用自动提交或容器管理的提交方法之一时,可使用此接口处理从Kafka消费者poll()操作接收的所有ConsumerRecord实例。
//使用此接口时,不支持AckMode.RECORD,因为已为侦听器提供了完整的批处理。提供了对Consumer对象的访问。
public interface BatchConsumerAwareMessageListener<K, V> extends BatchMessageListener<K, V> {
void onMessage(List<ConsumerRecord<K, V>> data, Consumer<?, ?> consumer);
}
//使用一种手动提交方法时,可使用此接口处理从Kafka消费者poll()操作接收的所有ConsumerRecord实例。提供了对Consumer对象的访问。
public interface BatchAcknowledgingConsumerAwareMessageListener<K, V> extends BatchMessageListener<K, V> {
void onMessage(List<ConsumerRecord<K, V>> data, Acknowledgment acknowledgment, Consumer<?, ?> consumer);
}
config
使用java配置:
@Configuration
@EnableKafka
public class KafkaConfig {
@Bean(name = "simpleFactory")
KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Integer, String>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(3);
factory.getContainerProperties().setPollTimeout(3000);
factory.getContainerProperties().setGroupId("test-group-1");
//当使用手动提交时必须设置ackMode为MANUAL,否则会报错No Acknowledgment available as an argument, the listener container must have a MANUAL AckMode to populate the Acknowledgment.
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL);
factory.getContainerProperties().setAckCount(10);
factory.getContainerProperties().setAckTime(10000);
return factory;
}
//@Bean
public ConsumerFactory<Integer, String> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
//@Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "39.108.107.152:9092,39.108.107.152:9091,39.108.107.152:9093");
//键的序列化方式
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
//值的序列化方式
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer
.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
return props;
}
}
consumer使用@kafkaListener时,需要通过containerFactory 指定配置containerFactory,通过bean Name指定,如下所示。
@Component
public class SimpleConsumer {
@KafkaListener(containerFactory = "simpleFactory", topics = "test")
public void testConsume(String content) {
System.out.println(content);
}
}
通过application.yml配置文件配置:
server:
port: 8701
spring:
application:
name: kafka
kafka:
bootstrap-servers: 39.108.107.152:9092,39.108.107.152:9091,39.108.107.152:9093
consumer:
group-id: kafka-consume-5
# auto-offset-reset: none
# auto-offset-reset: latest #最新的
auto-offset-reset: earliest #最早的
enable-auto-commit: false
producer:
acks: 1
listener:
#当enable-auto-commit为false时生效
ack-mode: manual
ack-count: 10 #当ackMode为“COUNT”或“COUNT_TIME”时,偏移提交之间的记录数
ack-time: 10000 #当ackMode为“TIME”或“COUNT_TIME”时,偏移提交之间的时间(以毫秒为单位)
该配置是默认containerFactory的配置,当我们不指定containerFactory 时则使用的是默认factory。
commit
如果enable.auto.commit使用者属性为true,则Kafka根据其配置自动提交偏移量
如果spring.kafka.consumer.enable-auto-commit设置为false,设置AckMode的值MANUAL
AckMode有以下几个
RECORD # 每处理一条commit一次
BATCH(默认) # 每次poll的时候批量提交一次,频率取决于每次poll的调用频率
TIME # 每次间隔ackTime的时间去commit
COUNT # 累积达到ackCount次的ack去commit
COUNT_TIME # ackTime或ackCount哪个条件先满足,就commit
MANUAL # listener负责ack,但是背后也是批量上去
MANUAL_IMMEDIATE # listner负责ack,每调用一次,就立即commit
批量消费
只需要设置batchListener参数为true,同时把enable-auto-commit设置为false。
java配置:
factory.setBatchListener(true);
...
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
@KafkaListener(containerFactory = "simpleFactory", topics = "test-topic")
public void testConsumer(List<ConsumerRecord<Integer, String>> records, Acknowledgment ack) {
...
}
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:zhoulk
来源:https://blog.teemor.xyz