Srping Boot项目集成spring-kafka实现kafka消息的发送和消费

欢迎访问作者个人网站,查看更多文章

添加依赖:

  <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
 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值