1.添加相关kafka依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
    </dependencies>

2.yml文件配置

kafka:
    bootstrap-servers: 10.88.40.156:9092
    producer:
      # 发生错误后,消息重发的次数。
      retries: 0
      #当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。
      batch-size: 16384
      # 设置生产者内存缓冲区的大小。
      buffer-memory: 33554432
      # 键的序列化方式
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      # 值的序列化方式
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      # acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。
      # acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
      # acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
      acks: 1
    consumer:
      # 自动提交的时间间隔 在spring boot 2.X 版本中这里采用的是值的类型为Duration 需要符合特定的格式,如1S,1M,2H,5D
      auto-commit-interval: 1S
      #groupId名称自己定义,但必须要写,不然会报错,如果当前组的信息消费完了,换组的话,偏移量offset会重置,会把topic里面的消息内容重新获取一遍
      group-id: kafka-group
      # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
      # latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)
      # earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
      auto-offset-reset: earliest
      # 是否自动提交偏移量,默认值是true,为了避免出现重复数据和数据丢失,可以把它设置为false,然后手动提交偏移量
      enable-auto-commit: false
      # 键的反序列化方式
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      # 值的反序列化方式
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
    listener:
      # 在侦听器容器中运行的线程数。
      concurrency: 5
      #假设你拉取了500条信息,ack-mode: manual_immediate代表你每从kafka里面拿到一条数据处理完就提交
      #而ack-mode: manual则是拉取处理完500条数据,一起提交
      ack-mode: manual_immediate
      missing-topics-fatal: false
      #默认是single的,加上可以批量消费
      type: batch

 3.kafkaProducer

/**
 * kafka producer
 */
@Slf4j
@Component
public class KafkaProducer {
 
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
 
    /**
     * 发送消息到kafka
     */
    public void sendMessage(String topic, String message) {
        System.err.println(message);
//        log.info("sendMessage to kafka ,topic =[{}],message=[{}]", topic, message);
        kafkaTemplate.send(topic, message);
    }
 
    /**
     * 发送消息到kafka
     */
    public void sendMessage(String topic, String partionKey, String message) {
        log.info("sendMessage to kafka ,topic =[{}],partionKey=[{}],message=[{}]", topic, partionKey, message);
        kafkaTemplate.send(topic, partionKey, message);
    }
 
}

4.kafkaConsumer

@Component
@Slf4j
public class KafkaConsumer{

    @KafkaListener(topics = "topic2")
    public List<User> consumer(ConsumerRecords<String,String> consumerRecord, Acknowledgment acknowledgment){
        List<User> kafkaMessage = new ArrayList<>();
        for (ConsumerRecord<String, String> record : consumerRecord) {
            if (StringUtils.isBlank(record.value())){
                log.info("consumer value is {}",record.value());
            }
            User user = JSON.parseObject(record.value(), User.class);
            kafkaMessage.add(user);
            //手动提交offset
            acknowledgment.acknowledge();
            log.info("topic is [{}],key is [{}],values is [{}],offset is [{}],partition is [{}]",
                    record.topic(),record.key(),record.value(),record.offset(),record.partition());
        }
        System.out.println(kafkaMessage);
        return kafkaMessage;
    }



/**
     * 监控多个topic主题中的某个分区,甚至是具体到某个分区的某个偏移量
     * @param record
     */
    @KafkaListener(groupId = "kafka-group-demo",topicPartitions = {
            //监控topic3主题的,0号和1号分区的消息
            @TopicPartition(topic = "topic3",partitions = {"0","1"}),
            @TopicPartition(topic = "topic2",partitions = "0",partitionOffsets = @PartitionOffset(partition = "1",initialOffset = "100"))}
            //concurrency--当你这个项目起来的时候,kafka会帮你在同组(kafka-group-demo)下创建2个消费者,建议小于分区数,因为一旦大于,多的消费者将不会运行
            ,concurrency = "2")
    public void consumer(ConsumerRecord<String,String> record){
        System.out.println(record.value());
    }
}

5.写个测试的demo

@RestController
@RequestMapping("/send")
public class KafkaController {

    @Autowired
    private KafkaProducer kafkaProducer;

    @GetMapping("/message")
    public void message(@RequestBody User user){
        kafkaProducer.sendMessage("topic2", JSON.toJSONString(user));
    }

}

6.启动zookeeper,kafka,然后进入topic主题里面(之前文章有过程)

7.测试

spring cloud stream kafka 手动消息确认_spring

topic2主题里面有刚刚用postMan发送的消息 

spring cloud stream kafka 手动消息确认_spring_02

consumer也消费到了 

spring cloud stream kafka 手动消息确认_spring_03

 end....

在学习kafka的过程中,遇到一个非常蛋疼的报错:

 Listener method could not be invoked with the incoming message

出现这个错误就在网上寻求帮助,说是加了


Acknowledgment acknowledgment参数必须要在yml配置文件里面加个配置


spring cloud stream kafka 手动消息确认_kafka_04

spring cloud stream kafka 手动消息确认_kafka_05

后来发现不行,最后才发现还要多加一个😓

spring cloud stream kafka 手动消息确认_java_06

 最后才完美解决