pom.xml
org.springframework.kafka
spring-kafka
2.3.6.RELEASE
生产者config
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaAdmin;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableKafka
public class KafaProducerConfig {
public final static String BOOTSTRAPSERVERS = "111.168.0.222:9092,111.168.0.222:9092,111.168.0.222:9092";
public final static String TOPIC = "topic_自定义主题";
// /**
// * 定义一个KafkaAdmin的bean,可以自动检测集群中是否存在topic,不存在则创建
// */
// @Bean
// public KafkaAdmin kafkaAdmin() {
// Map configs = new HashMap<>();
// // 指定多个kafka集群多个地址,例如:192.168.2.11,9092,192.168.2.12:9092,192.168.2.13:9092
// configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAPSERVERS);
// return new KafkaAdmin(configs);
// }
// /**
// * 创建 Topic
// */
// @Bean
// public NewTopic topicinfo() {
// // 创建topic,需要指定创建的topic的"名称"、"分区数"、"副本数量(副本数数目设置要小于Broker数量)"
// return new NewTopic(TOPIC, 8, (short) 2);
// }
/**
* Producer Template 配置
*/
@Bean(name="kafkaTemplate")
public KafkaTemplate kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
/**
* Producer 工厂配置
*/
@Bean
public ProducerFactory producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
/**
* Producer 参数配置
*/
@Bean
public Map producerConfigs() {
Map props = new HashMap<>();
// 指定多个kafka集群多个地址
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAPSERVERS);
// 重试次数,0为不启用重试机制
props.put(ProducerConfig.RETRIES_CONFIG, 0);
//同步到副本, 默认为1
// acks=0 把消息发送到kafka就认为发送成功
// acks=1 把消息发送到kafka leader分区,并且写入磁盘就认为发送成功
// acks=all 把消息发送到kafka leader分区,并且leader分区的副本follower对消息进行了同步就任务发送成功
props.put(ProducerConfig.ACKS_CONFIG, "1");
// 生产者空间不足时,send()被阻塞的时间,默认60s
props.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, 6000);
// 控制批处理大小,单位为字节
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 4096);
// 批量发送,延迟为1毫秒,启用该功能能有效减少生产者发送消息次数,从而提高并发量
props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
// 生产者可以使用的总内存字节来缓冲等待发送到服务器的记录
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 40960);
// 消息的最大大小限制,也就是说send的消息大小不能超过这个限制, 默认1048576(1MB)
props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG,1048576);
// 键的序列化方式
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
// 值的序列化方式
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
// 压缩消息,支持四种类型,分别为:none、lz4、gzip、snappy,默认为none。
// 消费者默认支持解压,所以压缩设置在生产者,消费者无需设置。
props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"none");
return props;
}
}
生产者发送消息
这里引用上一篇自定义aop发送消息代码
//初始化线程池
public static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors()*5,
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(20), //缓存线程
new ThreadPoolExecutor.AbortPolicy() //直接报异常不执行这个线程
);
//发送消息方法
public void send(String message) throws Throwable {
try{
EXECUTOR.execute(()->{
log.info("用户操作日志消息内容:"+message);
kafkaTemplate.send("topic_自定义主题", message).addCallback(new ListenableFutureCallback>() {
@Override
public void onFailure(Throwable ex) {
log.error("发送消息失败:"+ex.getMessage());
}
@Override
public void onSuccess(SendResult result) {
log.info("发送消息成功");
}
});
});
}catch (Exception e){
log.error(e.getMessage());
}
}
消费者config
package com.sf.gis.cbtruck.baseinfo.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import java.util.HashMap;
import java.util.Map;
/**
* kafka消费者配置
*/
@Configuration
@EnableKafka
public class KafkaConsumerConfig {
public final static String BOOTSTRAPSERVERS = "111.168.0.222:9092,111.168.0.222:9092,111.168.0.222:9092";
public final static String TOPIC = "topic_自定义主题";
public final static String GOUPID = "消费者_consumer_group";
@Bean
KafkaListenerContainerFactory> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory
factory = new ConcurrentKafkaListenerContainerFactory<>();
// 设置消费者工厂
factory.setConsumerFactory(consumerFactory());
// 消费者组中线程数量
factory.setConcurrency(3);
// 拉取超时时间
factory.getContainerProperties().setPollTimeout(3000);
// 当使用批量监听器时需要设置为true
factory.setBatchListener(true);
return factory;
}
@Bean
public ConsumerFactory consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
@Bean
public Map consumerConfigs() {
Map propsMap = new HashMap<>();
// Kafka地址
propsMap.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAPSERVERS);
//配置默认分组,这里没有配置+在监听的地方没有设置groupId,多个服务会出现收到相同消息情况
propsMap.put(ConsumerConfig.GROUP_ID_CONFIG, GOUPID);
// 是否自动提交offset偏移量(默认true)
propsMap.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
// 自动提交的频率(ms)
propsMap.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "100");
// Session超时设置
propsMap.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000");
//请求超时时间
propsMap.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, "15000");
// 键的反序列化方式
propsMap.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
// 值的反序列化方式
propsMap.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
// offset偏移量规则设置:
// (1)、earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
// (2)、latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
// (3)、none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
propsMap.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
return propsMap;
}
}
消费者消费
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class KafkaConsumerListener {
@Autowired
private TSystemApiLogService systemApiLogService;
//监听kafka消费
@KafkaListener(topics = "topic_cb_truck_operate_log", containerFactory = "kafkaListenerContainerFactory")
public void test(String message) {
try{
log.info("消费消息:"+message);
//转换为自己的log实体类
ApiLog apiLog = JSONObject.parseObject(message, TSystemApiLog.class);
apiLogService.insert(tSystemApiLog);
}catch (Exception e){
log.error("日志插入失败");
e.printStackTrace();
}
}
}
不用config配置,另外一种yml文件配置
spring:
kafka:
###########【Kafka集群】###########
bootstrap-servers: 192.168.0.196:9092,192.168.0.58:9092,192.168.0.51:9092
###########【初始化生产者配置】###########
producer:
retries: 0 #重试次数
# 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
acks: all
# 批量大小
batch-size: 16384
# 提交延时
properties:
linger:
ms: 0
# 生产端缓冲区大小
buffer-memory: 33554432
# Kafka提供的序列化和反序列化类
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
###########【初始化消费者配置】###########
consumer:
group-id: operate_log_consumer_group
# 当kafka中没有初始offset或offset超出范围时将自动重置offset
# earliest:重置为分区中最小的offset;
# latest:重置为分区中最新的offset(消费分区中新产生的数据);
# none:只要有一个分区不存在已提交的offset,就抛出异常;
auto-offset-reset: latest
# 是否自动提交offset
enable-auto-commit: true
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# 批量消费每次最多消费多少条消息
max-poll-records: 50
# 提交offset延时(接收到消息后多久提交offset)
auto:
commit:
interval:
ms: 1000
# 消费会话超时时间(超过这个时间consumer没有发送心跳,就会触发rebalance操作)
properties:
session:
timeout:
ms: 120000
# 消费请求超时时间
request:
timeout:
ms: 180000
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
listener:
missing-topics-fatal: false
# concurrency: 3
# ack-mode: MANUAL
type: batch