Springboot-整合kafka

Spring for Apache Kafka 版本对应关系:

Spring for Apache Kafka Version

Spring Integration for Apache Kafka Version

kafka-clients

Spring Boot

2.7.0-SNAPSHOT

5.4.x

2.7.0 (RC5)

2.4.x

2.6.x

5.3.x or 5.4.x

2.6.0

2.3.x or 2.4.x

2.5.x

3.3.x

2.5.1

2.3.x

2.4.x

3.2.x

2.4.1

2.2.x

2.3.x

3.2.x

2.3.1

2.2.x

2.2.x

3.1.x

2.0.1, 2.1.x, 2.2.x

2.1.x (End of Life)

2.1.x

3.0.x

1.0.2

2.0.x (End of Life)

1.3.x

2.3.x

0.11.0.x, 1.0.x

1.5.x (End of Life)

官网链接:Spring for Apache Kafka

一、pom文件

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

二、配置文件

server:
  port: 9000
# kafka
spring:
  kafka:
    #============== kafka ===================
    # 指定kafka server的地址,集群配多个,中间,逗号隔开
    bootstrap-servers: localhost:9092
    #=============== consumer  =======================
    consumer:
      # 指定默认消费者group id
      group-id: meng
      # earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
      # latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
      # none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
      auto-offset-reset: earliest
      # 指定消息key和消息体的编解码方式
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
    #=============== provider  =======================
    producer:
      # 指定消息key和消息体的编解码方式
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      # 每次批量发送消息的数量
      batch-size: 65536
      buffer-memory: 524288
      bootstrap-servers: localhost:9092

三、代码

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

/**
 * @ClassName: KafkaController
 * @Description: TODO
 * @Author: MengMeng
 * @Date: 2020/12/23 12:59 下午
 * @Version: v1.0
 */
@RestController
public class KafkaController {

    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * @Description: providerKafka方法是 生产者
     * @param: []
     * @return: java.lang.String
     * @auther: MengMeng
     * @date: 2020/12/23 7:32 下午
     */
    @RequestMapping("/kafka")
    public String providerKafka() {
        ListenableFuture future = kafkaTemplate.send("meng-email","001", "xxx@163.com");
        future.addCallback(new ListenableFutureCallback<SendResult<Integer, String>>() {
            public void onFailure(Throwable throwable) {
                System.out.println("失败");
            }

            public void onSuccess(SendResult<Integer, String> integerStringSendResult) {
                System.out.println("成功");
            }
        });
//		JDK1.8写法
//		future.addCallback(o -> System.out.println("send-消息发送成功:" + message),
//				throwable -> System.out.println("消息发送失败:" + message));
        return "success";
    }

    /**
     * @Description: listen方法是 消费者
     * @param: [consumer]
     * @return: void
     * @auther: MengMeng
     * @date: 2020/12/23 7:37 下午
     */
    @KafkaListener(topics = {"meng-email"})
    public void listen(ConsumerRecord<?, ?> consumer) {
        Optional<?> kafkaMessage = Optional.ofNullable(consumer.value());
        if (kafkaMessage.isPresent()) {
            Object message = kafkaMessage.get();
            System.out.println("record =" + consumer);
            System.out.println("message =" + message);
            System.out.println("topic名称:" + consumer.topic() + ",key:" +
                    consumer.key() + "," + "分区位置:" + consumer.partition()
                    + ", 下标" + consumer.offset());
        }
    }
}

消费者:

yml:

spring:
  kafka:
    producer:
      bootstrap-servers: kafka-0:9092
      batch-size: 100
    consumer:
      group-id: meng_kafka_0
      auto-offset-reset: latest
      bootstrap-servers: kafka-0:9092
      auto-commit-interval: 1000
      enable-auto-commit: false
      max-poll-records: 1000
KafkaListener:
  feedback:
    topics: topic_1
    autoStartup: true
    # 消费者数量
    concurrency: 5
    # 单消费者线程数量
    parallelism: 10
    # 监听的 topic
    groupId: meng_kafka_0

config:

@Configuration
public class KafkaConsumerConfiguration {
    @Bean
    public KafkaListenerContainerFactory<?> batchFactory(ConsumerFactory consumerFactory) {
        ConcurrentKafkaListenerContainerFactory<Integer, String> factory =
                new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory);
        factory.getContainerProperties().setPollTimeout(180000);
        //设置为批量消费,每个批次数量在Kafka配置参数中设置
        factory.setBatchListener(true);
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
        //设置手动提交ackMode
        return factory;
    }

    @Bean
    public ConsumerAwareListenerErrorHandler consumerAwareErrorHandler() {
        return (message, e, consumer) -> {
            e.printStackTrace();
            return null;
        };
    }

}
Listener:
/**
 * kafka 消费者
 *
 */
@Component
public class KafkaListener {
    private static final Gson GSON = new Gson();

    private static Integer concurrency;
    private static Boolean autoStartup;
    private static Integer parallelism;
    private static LinkedBlockingQueue<ForkJoinPool> forkJoinPoolList;

    final DisposeService disposeService;

    public KafkaFeedbackListener(DisposeService disposeService) {
        this.disposeService = disposeService;
    }

    @Value("${KafkaListener.feedback.concurrency}")
    public void setConcurrency(Integer concurrency) {
        KafkaFeedbackListener.concurrency = concurrency;
    }

    @Value("${KafkaListener.feedback.autoStartup}")
    public void setAutoStartup(Boolean autoStartup) {
        KafkaFeedbackListener.autoStartup = autoStartup;
    }

    @Value("${KafkaListener.feedback.parallelism}")
    public void setParallelism(Integer parallelism) {
        KafkaFeedbackListener.parallelism = parallelism;
    }

    /**
     * 初始化线程池
     */
    @PostConstruct
    private static void init() {
        log.debug("init forkJoinPoolList, concurrency is {},parallelism is {},autoStartup is {}",
                concurrency, parallelism, autoStartup);
        if (null == forkJoinPoolList && autoStartup) {
            forkJoinPoolList = new LinkedBlockingQueue();
            for (int i = 0; i < concurrency; i++) {
                forkJoinPoolList.add(new ForkJoinPool(parallelism));
            }
        }
        log.debug("init forkJoinPoolList,end");
    }

    @KafkaListener(topics = {"#{'${KafkaListener.feedback.topics}'.split(',')}"},
            autoStartup = "#{'${KafkaListener.feedback.autoStartup}'}",
            groupId = "#{'${KafkaListener.feedback.groupId}'}",
            concurrency = "#{'${KafkaListener.feedback.concurrency}'}",
            containerFactory = "batchFactory",
            errorHandler = "consumerAwareErrorHandler")
    public void listenToKafka(List<ConsumerRecord<String, String>> records, Acknowledgment ack) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        ForkJoinPool finalForkJoinPool = getForkJoinPool();
        try {
            CompletableFuture[] completableFutures = records.stream().map(it -> CompletableFuture.runAsync(() -> {
                //demoService.demo(GSON.fromJson(it.value(), Demo.class));
            }, finalForkJoinPool).exceptionally(e -> {
                log.error("topic处理异常:{}", e);
                return null;
            })).toArray(CompletableFuture[]::new);

            CompletableFuture.allOf(completableFutures).exceptionally(e -> {
                log.error("topic allOf异常:{}", e);
                return null;
            }).get();
            ack.acknowledge();
        } finally {
            addForkJoinPool(finalForkJoinPool);
            long end = System.currentTimeMillis();
            log.info("feedback listener finally,time:{} ,avg:{}", end - start, (end - start) / records.size());
            records.clear();
        }
    }

    private ForkJoinPool getForkJoinPool() {
        return forkJoinPoolList.poll();
    }

    private void addForkJoinPool(ForkJoinPool forkJoinPool) {
        forkJoinPoolList.add(forkJoinPool);
    }

}

相关链接:

kafka是否自启动:springboot之kafkaListener手动开启与暂停_BrighteStar的博客-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值