只是一个模板,酌情修改

依赖:

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

配置文件内容(部分为自定义):

spring:
  kafka:
    producer:
      #消息发送失败重试次数,默认是int最大值
      retries: 10
      #重试间隔
      retry_backoff_ms: 500
      #压缩消息,支持四种类型,分别为:none、lz4、gzip、snappy,默认为none,lz4压缩比最高
      compression_type: none
      #提高并发量
      linger_ms: 30
      #失败重试时,保证消息顺序性,会降低吞吐量
      max_in_flight_requests_per_connection: 1
      #开启发送消息幂等性(单分区)
      enable_idempotence: true
      #key、value序列化器选择
      key_serializer: org.apache.kafka.common.serialization.StringSerializer
      value_serializer: org.apache.kafka.common.serialization.StringSerializer
      #生产者空间不足时阻塞的时间,默认60s
      max_block_ms: 6000
      #acks = 0 如果设置为零,则生产者将不会等待来自服务器的任何确认,该记录将立即添加到套接字缓冲区并视为已发送。在这种情况下,无法保证服务器已收到记录,并且重试配置将不会生效(因为客户端通常不会知道任何故障),为每条记录返回的偏移量始终设置为-1。
      #acks = 1 这意味着leader会将记录写入其本地日志,但无需等待所有副本服务器的完全确认即可做出回应,在这种情况下,如果leader在确认记录后立即失败,但在将数据复制到所有的副本服务器之前,则记录将会丢失。
      #acks = all 这意味着leader将等待完整的同步副本集以确认记录,这保证了只要至少一个同步副本服务器仍然存活,记录就不会丢失,这是最强有力的保证,这相当于acks = -1的设置。
      acks: -1
      #压测后给出最适合的值,当前为默认值(批处理字节大小,太大可能oom,或者消息不能及时发送到broker,太小性能不足)
      batch-size: 16384
    # 指定 kafka 地址可以多个
    bootstrap-servers: 10.161.19.110:9092
    # 指定listener 容器中的线程数,用于提高并发量
    listener:
      #是否开启批量处理
      batch_listener: true
      #线程数
      concurrency: 3
      #手动提交的方式,当enable-auto-commit: false时起作用
      #manual:手动调用Acknowledgment.acknowledge()后立即提交
      #record:当每一条记录被消费者监听器(ListenerConsumer)处理之后提交
      #batch:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
      #time: 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间大于TIME时提交
      #count:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量大于等于COUNT时提交
      #count_time:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后, 手动调用Acknowledgment.acknowledge()后提交
      ack-mode: manual
      #消费超时时间
      poll-timeout: 3000
    # 消费者的配置
    consumer:
#      properties:
#        sasl-mechanism: PLAIN
#        security-protocol: SASL_PLAINTEXT
#        sasl-jaas-config: org.apache.kafka.common.security.plain.PlainLoginModule required username='kafka' password='kafka';
      # 指定默认消费者group id
      group-id: test-cy
      #earliest
      #当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
      #latest
      #当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
      #none
      #topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
      auto-offset-reset: earliest
      # 是否开启自动提交
      enable-auto-commit: false
      # key,value的反序列化方式
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      #消费者默认等待服务响应时间(毫秒)
      fetch-max-wait: 5000

获取配置类(四个):(动态的从配置中心获取)

一、KafkaBaseProp.java(基础的配置)

/**
 * kafka集群配置
 * */
@Component
@RefreshScope
@Data
@ConfigurationProperties(prefix = "spring.kafka")
public class KafkaBaseProp {
    /**
     *服务器集群地址,逗号隔开
     * */
    private String bootstrapServers;

}

二、KafkaConsumerProp.java(消费者配置)

/**
 * kafka的消费者配置
 * */
@Component
@RefreshScope
@Data
@ConfigurationProperties(prefix = "spring.kafka.consumer")
public class KafkaConsumerProp {
    /**
     * 指定默认消费者group id
     * */
    private String groupId;
    /**
     *    earliest 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
     *    latest  当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
     *    none topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
     */
    private String autoOffsetReset;
    /**
     * 是否开启自动提交
     * */
    private Boolean enableAutoCommit;
    /**
     * key的反序列化器
     * */
    private String keyDeserializer;
    /**
     * value的反序列化器
     * */
    private String valueDeserializer;
    /**
     * 消费者默认等待服务响应时间(毫秒)
     * */
    private String fetchMaxWait;
}

三、KafkaListenerProp.java(监听配置)

/**
 * kafka的监听配置
 * */
@Component
@RefreshScope
@Data
@ConfigurationProperties(prefix = "spring.kafka.listener")
public class KafkaListenerProp {

    /**
     * 启用线程数(提高并发)
     * */
    private Integer concurrency;
    /**
     *  手动提交的方式,当enable-auto-commit: false时起作用
     *  manual:手动调用Acknowledgment.acknowledge()后立即提交
     *  record:当每一条记录被消费者监听器(ListenerConsumer)处理之后提交
     *  batch:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
     *  time: 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间大于TIME时提交
     *  count:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量大于等于COUNT时提交
     *  count_time:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后, 手动调用Acknowledgment.acknowledge()后提交
     * */
    private String ackMode;
    /**
     * 消费超时时间
     */
    private Long pollTimeout;
    /**
     * 是否开启批量处理
     * */
    private Boolean batchListener;
}

四、KafkaProducerProp.java(生产者配置)

/**
 *kafka的生产者配置
 * */
@Component
@RefreshScope
@Data
@ConfigurationProperties(prefix = "spring.kafka.producer")
public class KafkaProducerProp {
    /**
     * 批量发送,延迟为30毫秒,如果30ms内凑不够batch则强制发送,提高并发
     * */
    private String lingerMs;

    /**
     * 失败重试时,保证消息顺序性,会降低吞吐量
     * */
    private String maxInFlightRequestsPerConnection;
    /**
     *acks = 0 如果设置为零,则生产者将不会等待来自服务器的任何确认,该记录将立即添加到套接字缓冲区并视为已发送。在这种情况下,无法保证服务器已收到记录,并且重试配置将不会生效(因为客户端通常不会知道任何故障),为每条记录返回的偏移量始终设置为-1。
     *acks = 1 这意味着leader会将记录写入其本地日志,但无需等待所有副本服务器的完全确认即可做出回应,在这种情况下,如果leader在确认记录后立即失败,但在将数据复制到所有的副本服务器之前,则记录将会丢失。
     *acks = all 这意味着leader将等待完整的同步副本集以确认记录,这保证了只要至少一个同步副本服务器仍然存活,记录就不会丢失,这是最强有力的保证,这相当于acks = -1的设置。
     * */
    private String acks;
    /**
     *压测后给出最适合的值,当前为默认值(批处理字节大小,太大可能oom,或者消息不能及时发送到broker,太小性能不足)
     * */
    private String batchSize;
    /**
     * enable_idempotence开启消息幂等性
     * */
    private Boolean enableIdempotence;
    /**
     *生产者空间不足时阻塞的时间
     */
    private String maxBlockMs;
    /**
     * 消息压缩类型
     */
    private String compressionType;
    /**
     * 消息发送失败重试次数
     * */
    private String retries;
    /**
     * 重试间隔
     * */
    private String retryBackoffMs;
    /**
     * key、value的序列化器
     * */
    private String keySerializer;
    private String valueSerializer;

}

配置kafkatemplate 的Bean (appilcation.yml中设置

KafkaTemplateConfig:
  isOpen: true
)

然后

/**
 * kafkatemplateBean配置
 * */
@Configuration
@Slf4j
@ConditionalOnProperty(prefix="KafkaTemplateConfig",name = "isOpen",havingValue = "true")
public class KafkaTemplateConfig {

    @Autowired
    private KafkaBaseProp kafkaBaseProp;
    @Autowired
    private KafkaConsumerProp kafkaConsumerProp;
    @Autowired
    private KafkaProducerProp kafkaProducerProp;
    @Autowired
    private KafkaListenerProp kafkaListenerProp;
    /**
     * Producer Template 配置
     */
    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }

    /**
     * Producer 工厂配置
     */
    @Bean
    @RefreshScope
    public ProducerFactory<String, String> producerFactory() {
        return new DefaultKafkaProducerFactory<>(producerConfigs());
    }

    /**
     * Producer 参数配置
     */
    @Bean
    public Map<String, Object> producerConfigs() {
        Map<String, Object> props = new ConcurrentHashMap<>();
        //kafka地址
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaBaseProp.getBootstrapServers());
        //保证幂等性、消息顺序性
        props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, kafkaProducerProp.getMaxInFlightRequestsPerConnection());
        //只能保证单分区上的幂等性
        props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, kafkaProducerProp.getEnableIdempotence());
        /**
         *acks = 0 如果设置为零,则生产者将不会等待来自服务器的任何确认,该记录将立即添加到套接字缓冲区并视为已发送。在这种情况下,无法保证服务器已收到记录,并且重试配置将不会生效(因为客户端通常不会知道任何故障),为每条记录返回的偏移量始终设置为 - 1。
         *acks = 1 这意味着leader会将记录写入其本地日志,但无需等待所有副本服务器的完全确认即可做出回应,在这种情况下,如果leader在确认记录后立即失败,但在将数据复制到所有的副本服务器之前,则记录将会丢失。
         *acks = all 这意味着leader将等待完整的同步副本集以确认记录,这保证了只要至少一个同步副本服务器仍然存活,记录就不会丢失,这是最强有力的保证,这相当于acks = -1 的设置。
         */
        props.put(ProducerConfig.ACKS_CONFIG, kafkaProducerProp.getAcks());
        //(调优项)压测后给出最适合的值,当前为默认值(批处理字节大小,太大可能oom,或者消息不能及时发送到broker,太小性能不足)
        props.put(ProducerConfig.BATCH_SIZE_CONFIG, kafkaProducerProp.getBatchSize());
        // 生产者空间不足时,send()被阻塞的时间,默认60s
        props.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, kafkaProducerProp.getMaxBlockMs());
        // 批量发送,延迟为30毫秒,如果30ms内凑不够batch则强制发送,提高并发
        props.put(ProducerConfig.LINGER_MS_CONFIG, kafkaProducerProp.getLingerMs());
        // 消息的最大大小限制,也就是说send的消息大小不能超过这个限制, 默认1048576(1MB)
        props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG,1048576);
        // 压缩消息,支持四种类型,分别为:none、lz4、gzip、snappy,默认为none。
        // 消费者默认支持解压,所以压缩设置在生产者,消费者无需设置。
        props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,kafkaProducerProp.getCompressionType());
        //消息发送失败重试次数(默认是Integer.MAX_VALUE)
        props.put(ProducerConfig.RETRIES_CONFIG,kafkaProducerProp.getRetries());
        //重试间隔时间(ms)
        props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG,kafkaProducerProp.getRetryBackoffMs());
        //key、value的序列化方式
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,kafkaProducerProp.getKeySerializer());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, kafkaProducerProp.getValueSerializer());
        return props;
    }

    /**
     * 监听工厂
     * */
    @Bean
    KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Object, Object>> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        //线程数
        factory.setConcurrency(kafkaListenerProp.getConcurrency());
        //手动提交
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL);
        //开启批量处理
        factory.setBatchListener(kafkaListenerProp.getBatchListener());
        factory.getContainerProperties().setPollTimeout(kafkaListenerProp.getPollTimeout());
        return factory;
    }
    /**
     * kafka消费者工厂
     * */
    @Bean
    public ConsumerFactory<Object, Object> consumerFactory() {
        return new DefaultKafkaConsumerFactory(consumerConfigs());
    }

    @Bean
    public Map consumerConfigs() {
        Map<String,Object> props = new ConcurrentHashMap();
        //配置地址
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaBaseProp.getBootstrapServers());
        //key、value的反序列化方式
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, kafkaConsumerProp.getKeyDeserializer());
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, kafkaConsumerProp.getValueDeserializer());
        //消费者组
        props.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaConsumerProp.getGroupId());
        //是否开启自动提交
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, kafkaConsumerProp.getEnableAutoCommit());
        //消费策略
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, kafkaConsumerProp.getAutoOffsetReset());
        //消费者默认等待服务响应时间(毫秒)
        props.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, kafkaConsumerProp.getFetchMaxWait());
        return props;
    }
}

封装发送消息service:

@Service
public class KafkaProducerService {
    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * producer 同步方式发送数据
     *
     * @param topic   topic名称
     * @param message producer发送的数据
     */
    public void sendMessageSync(String topic, String message) throws InterruptedException, ExecutionException, TimeoutException {
        kafkaTemplate.send(topic, message).get(5, TimeUnit.SECONDS);
    }

    /**
     * producer 异步方式发送数据
     *
     * @param topic   topic名称
     * @param message producer发送的数据
     */
    public void sendMessageAsync(String topic, String message) {
        kafkaTemplate.send(topic, message).addCallback(new ListenableFutureCallback() {
            @Override
            public void onSuccess(Object o) {
                ProducerRecord producerRecord;
                System.out.println("消息发送成功");
            }

            @Override
            public void onFailure(Throwable throwable) {
                System.out.println("发送失败");
            }
        });
    }
}

发送消息:(相应对象自己封装序列化反序列化value,这里使用简单的String测试)

调用:(批量消费)

kafkaProducerService.sendMessageAsync("test","hello"+i);

消费者

@Component
public class KafkaConsumer {

    @KafkaListener(topics = {"test"},groupId = "test-cy",containerFactory = "kafkaListenerContainerFactory")
    public void kafkalistener(List<ConsumerRecord<?, ?>> records, Acknowledgment ack){

        for(ConsumerRecord item:records)
        {
            System.out.printf("topic is %s, offset is %d,partition is %s, value is %s \n", item.topic(), item.offset(),item.partition(), item.value());

        }
        ack.acknowledge();
    }
}
这样在进行调试的时候可以支持动态的调整参数实现热部署的调优,参数也比较全,就是配置上麻烦了点。