org.springframework.kafka
spring-kafka
KafkaTemplateConfig:
isOpen: true
spring:
kafka:
#指定kafka地址可以多个
bootstrap-servers: 192.168.99.100:9092
producer:
#消息发送失败重试次数
retries: 3
#重试间隔
retry_backoff_ms: 500
#压缩消息,支持四种类型,分别为:none、lz4、gzip、snappy,默认为none,lz4压缩比最高
compression-type: none
#发送缓冲区大小32M
buffer-memory: 33554432
#去缓冲区中一次拉16k的数据,发送到broker
batch-size: 16384
#每条消息大小限制 1M
max-request_size: 1048576
#设置发送延时时间,如果在设置的时间内依然没有达到batch-size,依然发出消息给kafka集群
linger_ms: 30
#失败重试时,保证消息顺序性,会降低吞吐量
max_in_flight_requests_per_connection: 1
#开启发送消息幂等性(单分区)
enable_idempotence: true
#生产者空间不足时阻塞的时间,默认60s
max_block_ms: 6000
#acks = 0 如果设置为零,则生产者将不会等待来自服务器的任何确认,该记录将立即添加到套接字缓冲区并视为已发送。在这种情况下,无法保证服务器已收到记录,并且重试配置将不会生效(因为客户端通常不会知道任何故障),为每条记录返回的偏移量始终设置为-1。
#acks = 1 这意味着leader会将记录写入其本地日志,但无需等待所有副本服务器的完全确认即可做出回应,在这种情况下,如果leader在确认记录后立即失败,但在将数据复制到所有的副本服务器之前,则记录将会丢失。
#acks = all 这意味着leader将等待完整的同步副本集以确认记录,这保证了只要至少一个同步副本服务器仍然存活,记录就不会丢失,这是最强有力的保证,这相当于acks = -1的设置。
acks: -1
#key,value序列化器选择
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
listener:
#是否开启批量处理
batch_listener: true
#线程数
concurrency: 3
#RECORD 当每⼀条记录被消费者监听器(ListenerConsumer)处理之后提交
#BATCH 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
#TIME 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间⼤于TIME时提交
#COUNT 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量⼤于等于COUNT时提交
#COUNT_TIME TIME | COUNT 有⼀个条件满⾜时提交
#MANUAL 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后, ⼿动调⽤Acknowledgment.acknowledge()后提交
#MANUAL_IMMEDIATE ⼿动调⽤Acknowledgment.acknowledge()后⽴即提交,⼀般使⽤这种
ack-mode: manual_immediate
#消费超时时间
poll-timeout: 3000
consumer:
#指定默认消费者group id
group-id: testGroup
#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
# properties:
# sasl-mechanism: PLAIN
# security-protocol: SASL_PLAINTEXT
# sasl-jaas-config: org.apache.kafka.common.security.plain.PlainLoginModule required username='kafka' password='kafka';
/**
* kafka集群配置
* */
@Component
@Data
@ConfigurationProperties(prefix = "spring.kafka")
public class KafkaBaseProp {
/**
*服务器集群地址,逗号隔开
* */
private String bootstrapServers;
}
/**
* kafka的消费者配置
* */
@Component
@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;
}
/**
* kafka的监听配置
* */
@Component
@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;
}
package com.lb.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@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;
/**
* 缓存区大小
*/
private String bufferMemory;
/**
* 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;
/**
* 消息大小限制
*/
private String maxRequestSize;
}
KafkaTemplateConfig:
isOpen: true
package com.lb.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.*;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.kafka.listener.ContainerProperties;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
@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 kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
/**
* Producer 工厂配置
*/
@Bean
public ProducerFactory producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
/**
* Producer 参数配置
*/
@Bean
public Map producerConfigs() {
Map 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());
//去缓冲区中一次拉16k的数据,发送到broker
props.put(ProducerConfig.BATCH_SIZE_CONFIG, kafkaProducerProp.getBatchSize());
//设置缓存区大小
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, kafkaProducerProp.getBufferMemory());
// 生产者空间不足时,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, kafkaProducerProp.getMaxRequestSize());
// 压缩消息,支持四种类型,分别为: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> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory
private final static String TOPIC_NAME = "my-replicated-topic";
@Autowired
private KafkaTemplate kafkaTemplate;
@RequestMapping("/send")
public String send() {
kafkaTemplate.send(TOPIC_NAME, 0, "key", "this is a message");
return "send success";
}
@Component
public class MyConsumer {
@KafkaListener(topics = "my-replicated-topic", groupId = "MyGroup1", containerFactory = "kafkaListenerContainerFactory")
public void kafkalistener(List> 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();
}
}
此Consumer只能接受List