Kafka — 基于SpringBoot集成Kafka实践(三)

1、基础环境:

springboot version:springboot2.3.4.RELEASE
kafka version :2.6.1
Apache Kafka Clients 2.4.1

Spring Framework 5.3.x

Minimum Java version: 8
spring-kafka 将核心Spring概念应用于基于Kafka的消息传递解决方案的开发。它提供了一个“模板”作为发送消息的高级抽象。它还为带有@KafkaListener注解和“KafkaMessageListenerContainer”的消息驱动的POJO提供支持。这些库促进了依赖注入和声明式的使用。

特征

  • KafkaTemplate
  • KafkaMessageListenerContainer
  • @KafkaListener
  • KafkaTransactionManager
  • spring-kafka-test (jar with embedded kafka server)

当使用maven或gradle进行版本管理时,引用以下这些版本:

  • Spring Boot 1.5(EOL)用户应使用1.3.x(Boot依赖管理默认情况下将使用1.1.x,因此应予以覆盖)。
  • Spring Boot 2.1用户应使用2.2.x(引导依赖项管理将使用正确的版本)。
  • Spring Boot 2.2用户应使用2.3.x(引导依赖性管理将使用正确的版本)或将版本覆盖为2.4.x)。
  • Spring Boot 2.3用户应使用2.5.x(引导依赖项管理将使用正确的版本)。
  • Spring Boot 2.4用户应该使用2.6.x(Boot依赖管理将使用正确的版本)。

在Boot 2.3.x上使用2.6.0;

2、引入依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

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

spring-kafka 包含了 kafka-client,以为为Kafka客户端兼容性:
Kafka — 基于SpringBoot集成Kafka实践(三)_第1张图片

spring boot会自动配置kafka,接下来只要配置yml属性文件和主题名配置;

3、配置application.yml

server:
  servlet:
    context-path: /
  port: 18080
spring:
  kafka:
    bootstrap-servers: 192.168.174.100:9092
    #生产者的配置,大部分我们可以使用默认的,这里列出几个比较重要的属性
    producer:
      #每批次发送消息的数量
      batch-size: 16
      #设置大于0的值将使客户端重新发送任何数据,一旦这些数据发送失败。注意,这些重试与客户端接收到发送错误时的重试没有什么不同。允许重试将潜在的改变数据的顺序,如果这两个消息记录都是发送到同一个partition,则第一个消息失败第二个发送成功,则第二条消息会比第一条消息出现要早。
      retries: 0
      #producer可以用来缓存数据的内存大小。如果数据产生速度大于向broker发送的速度,producer会阻塞或者抛出异常,以“block.on.buffer.full”来表明。这项设置将和producer能够使用的总内存相关,但并不是一个硬性的限制,因为不是producer使用的所有内存都是用于缓存。一些额外的内存会用于压缩(如果引入压缩机制),同样还有一些用于维护请求。
      buffer-memory: 33554432
      #key序列化方式
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      topic:
	    group-id: topicGroupId
	    topic-name:
	      - topic1
	      - topic2
	      - topic3
    #消费者的配置
    consumer:
      #Kafka中没有初始偏移或如果当前偏移在服务器上不再存在时,默认区最新 ,有三个选项 【latest, earliest, none】
      auto-offset-reset: latest
      #是否开启自动提交
      enable-auto-commit: true
      #自动提交的时间间隔
      auto-commit-interval: 100
      #key的解码方式
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      #value的解码方式
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      #在/usr/local/etc/kafka/consumer.properties中有配置
      #group-id: test-group

logging:
  file: /kafka-demo-logs/${spring.application.name}/root.log

测试代码:

@RestController
@Slf4j
public class KafkaController {
    @Autowired
    private KafkaTemplate<Object,Object> template;
    @GetMapping("/send/{input}")
    public void sendMsg(@PathVariable String input){
       log.info("this msg send is : {}",input);
       this.template.send("topic_input",input);
    }
    @KafkaListener(id = "test_group",topics = "topic_input")
    public void listenMsg(String input){
        log.info("input value is : {}",input);
    }
}

4、新建KafkaTopicProperties

@ConfigurationProperties("kafka.topic")
public class KafkaTopicProperties implements Serializable {

    private String groupId;
    private String[] topicName;

    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId;
    }

    public String[] getTopicName() {
        return topicName;
    }

    public void setTopicName(String[] topicName) {
        this.topicName = topicName;
    }

5、添加KafkaTopicConfiguration

@Configuration
@EnableConfigurationProperties(KafkaTopicProperties.class)
public class KafkaTopicConfiguration {

    private final KafkaTopicProperties properties;

    public KafkaTopicConfiguration(KafkaTopicProperties properties) {
        this.properties = properties;
    }

    @Bean
    public String[] kafkaTopicName() {
        return properties.getTopicName();
    }

    @Bean
    public String topicGroupId() {
        return properties.getGroupId();
    }

}

6、生产消费测试

生产者:
kafkaTemplate提供了一个回调方法addCallback,我们可以在回调方法中监控消息是否发送成功 或 失败时做补偿处理;
消费者:
1、指定topic、partition、offset消费
前面我们在监听消费topic1的时候,监听的是topic1上所有的消息,如果我们想指定topic、指定partition、指定offset来消费呢?也很简单,@KafkaListener注解已全部为我们提供:

@KafkaListener(id = "consumer1",groupId = "felix-group",topicPartitions = {
        @TopicPartition(topic = "topic1", partitions = { "0" }),
        @TopicPartition(topic = "topic2", partitions = "0", partitionOffsets = @PartitionOffset(partition = "1", initialOffset = "8"))
})
public void onMessage2(ConsumerRecord<?, ?> record) {
    System.out.println("topic:"+record.topic()+"|partition:"+record.partition()+"|offset:"+record.offset()+"|value:"+record.value());
}

属性解释:

① id:消费者ID;

② groupId:消费组ID;

③ topics:监听的topic,可监听多个;

④ topicPartitions:可配置更加详细的监听信息,可指定topic、parition、offset监听。

注意:topics和topicPartitions不能同时使用;

测试service

@Service
public class IndicatorService {

    private Logger LOG = LoggerFactory.getLogger(IndicatorService.class);

    private final KafkaTemplate<Integer, String> kafkaTemplate;

    /**
     * 注入KafkaTemplate
     * @param kafkaTemplate kafka模版类
     */
    @Autowired
    public IndicatorService(KafkaTemplate kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }


    @KafkaListener(topics = "#{kafkaTopicName}", groupId = "#{topicGroupId}")
    public void processMessage(ConsumerRecord<Integer, String> record) {
        LOG.info("kafka processMessage start");
        LOG.info("processMessage, topic = {}, msg = {}", record.topic(), record.value());

        // do something ...

        LOG.info("kafka processMessage end");
    }

    public void sendMessage(String topic, String data) {
        LOG.info("kafka sendMessage start");
        ListenableFuture<SendResult<Integer, String>> future = kafkaTemplate.send(topic, data);
        future.addCallback(new ListenableFutureCallback<SendResult<Integer, String>>() {
            @Override
            public void onFailure(Throwable ex) {
                LOG.error("kafka sendMessage error, ex = {}, topic = {}, data = {}", ex, topic, data);
            }

            @Override
            public void onSuccess(SendResult<Integer, String> result) {
                LOG.info("kafka sendMessage success topic = {}, data = {}",topic, data);
            }
        });
        LOG.info("kafka sendMessage end");
    }
}

你可能感兴趣的:(分布式,kafka,springboot,kafka,java,spring,boot)