Kafka踩坑记

重试消费

之前认为手动提交可以重试消费

之前一直认为将消费端设置为手动提交,如果消费者方法程序因为逻辑异常就不提交当前的offset,只有正常执行完毕才提交,认为不提交的offset会自动放到kafka自动重试再次消费,然而其实并非如此,Consumder程序消费的offset只有在程序第一次启动采取服务端(_consumer_offsets队列中获取),然后在此基础上递增offset进行消费,也就是offset是一直增长的,不管是否有的消息没有被提交,如果有的offset没有提交,会在程序重新启动时,会去拉取那些没有提交的offset进行消费

通过异常的方式实现重复消费

所以手动提交无法实现,就通过在程序中手动抛异常的方式,然后通过设置setErrorHandler捕获到异常进行重试的,代码 配置如下:

异常可以拿到consumerRecord 消息对象,进而获取到所属队列,消息内容,然后就可以将该消息传入其他队列(比如自定义的死信队列)进行再次消费或者记录日志

Configuration
public class KafkaAckCusumerConfig {
    /***
     * broker服务器地址
     */
    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;
    /***
     * producer 序列化、反序列化配置
     */
    @Value("${spring.kafka.producer.key-serializer}")
    private  String producerKeySer;
    @Value("${spring.kafka.producer.value-serializer}")
    private  String producerValSer;

    /***
     * group_id
     */
    @Value("${spring.kafka.consumer.group-id}")
    private String groupId;
    /***
     *consumer 序列化、反序列化配置
     */
    @Value("${spring.kafka.consumer.key-deserializer}")
    private String consumerKeySer;
    @Value("${spring.kafka.consumer.value-deserializer}")
    private String consumerValSer;
    /***
     * 每次最大拉去的消费数目
     */
    @Value("${spring.kafka.consumer.max-poll-records}")
    private  String maxPollRecords;
    /***
     * 超时时间
     */
    @Value("${spring.kafka.consumer.session.timeout}")
    private String sessionTimeout;
    /***
     * 心跳阻塞时间
     */
    @Value("${spring.kafka.consumer.heartbeat.interval}")
    private  String heartBeatInter;
    /***
     * 每次 poll 的允许最大阻塞时间, 单位 ms
     */
    @Value("${spring.kafka.consumer.pollTimeout}")
    private String pollTimeOut;
        private static final Logger log = LoggerFactory.getLogger(ClusterStatePublisher.AckListener.class);

        private Map<String, Object> consumerProps() {

            Map<String, Object> props = new HashMap<>();
            props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
            props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,consumerKeySer);
            props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,consumerValSer);
            props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,maxPollRecords);
            props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG,sessionTimeout);
            props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG,heartBeatInter);
            props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true);
            props.put(ConsumerConfig.GROUP_ID_CONFIG,"kcy");
            props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");

            return props;

        }

        @Bean("ackContainerFactory")
        public ConcurrentKafkaListenerContainerFactory ackContainerFactory() {

            ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory();

            factory.setConsumerFactory(new DefaultKafkaConsumerFactory(consumerProps()));


            // 最大重试次数3次
            SeekToCurrentErrorHandler seekToCurrentErrorHandler = new SeekToCurrentErrorHandler((consumerRecord, e) -> {
                log.error("队列:{}异常,消息内容:{}.抛弃这个消息==,{}", consumerRecord.topic(),consumerRecord.toString(), e);
            }, new FixedBackOff(1000, 3));
            factory.setErrorHandler(seekToCurrentErrorHandler);


            // ack模式,详细见下文注释
           // factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);

            return factory;

        }


}

auto.offset.reset

earliest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费。
latest
当创建一个新分组的消费者时,auto.offset.reset值为latest时,表示消费新的数据(从consumer创建开始,后生产的数据),之前产生的数据不消费。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值