Kafka 详解大全
目录
Kafka介绍
什么是Kafka
Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,现在是一个Apache开源项目。Kafka具有高吞吐量、分布式、可扩展、持久化、容错等特性,广泛应用于实时数据流处理、日志收集、事件溯源、消息队列等场景。
核心特性
核心特性 :
- 高吞吐量 : 单机每秒可处理数十万条消息
- 分布式 : 支持水平扩展,可部署在多个服务器上
- 持久化 : 消息持久化到磁盘,支持数据备份
- 容错性 : 支持副本机制,单点故障不影响服务
- 实时性 : 支持实时流处理,延迟通常在毫秒级
- 可扩展性 : 支持动态添加和删除节点
- 多客户端支持 : 支持多种编程语言的客户端
版本发展
版本历史 :
- Kafka 0.8 : 2013年,引入副本机制
- Kafka 0.9 : 2014年,引入安全特性
- Kafka 0.10 : 2015年,引入Streams API
- Kafka 0.11 : 2017年,引入事务支持
- Kafka 1.0 : 2017年,第一个生产就绪版本
- Kafka 2.0 : 2018年,性能提升和新特性
- Kafka 2.8 : 2021年,移除ZooKeeper依赖
- Kafka 3.0 : 2021年,KRaft模式正式版
- Kafka 3.4 : 2023年,最新稳定版本
应用场景
1. 日志收集
日志收集场景 :
系统日志 :
- 应用日志收集
- 系统监控日志
- 错误日志聚合
- 性能指标收集
用户行为日志 :
- 用户点击日志
- 页面访问日志
- 操作行为日志
- 搜索查询日志
业务日志 :
- 订单操作日志
- 支付交易日志
- 库存变更日志
- 用户注册登录日志
2. 流处理
流处理场景 :
实时计算 :
- 实时统计计算
- 实时推荐系统
- 实时风控系统
- 实时监控告警
数据转换 :
- 数据格式转换
- 数据清洗过滤
- 数据聚合计算
- 数据路由分发
事件处理 :
- 事件驱动架构
- 复杂事件处理
- 事件溯源
- 事件回放
3. 消息队列
消息队列场景 :
异步处理 :
- 任务异步执行
- 事件异步处理
- 通知异步发送
- 数据异步同步
解耦服务 :
- 微服务间通信
- 系统模块解耦
- 接口解耦
- 数据解耦
流量削峰 :
- 高峰期流量缓冲
- 突发流量处理
- 系统负载均衡
- 资源使用优化
4. 数据管道
数据管道场景 :
数据同步 :
- 数据库同步
- 文件系统同步
- 缓存同步
- 搜索引擎同步
数据迁移 :
- 历史数据迁移
- 系统升级迁移
- 数据格式迁移
- 存储介质迁移
数据备份 :
- 实时数据备份
- 增量数据备份
- 数据版本管理
- 灾难恢复
架构结构图
整体架构
消费者组
Topic分区
Kafka集群
Consumer Group1
Consumer Group2
Topic1-Partition0
Topic1-Partition1
Topic2-Partition0
Topic2-Partition1
Broker1
Broker2
Broker3
ZooKeeper/KRaft
生产者
Kafka集群
消费者
详细组件架构
架构组件 :
生产者(Producer) :
- 创建消息
- 选择分区策略
- 发送消息到Broker
- 处理发送确认
Broker :
- 消息存储和管理
- 分区复制和同步
- 请求处理和路由
- 元数据管理
Topic :
- 消息分类容器
- 支持多个分区
- 可配置保留策略
- 支持压缩和清理
分区(Partition) :
- 消息存储单元
- 支持顺序保证
- 可配置副本数
- 支持水平扩展
消费者(Consumer) :
- 从分区消费消息
- 维护消费偏移量
- 支持消费者组
- 处理消息确认
集群架构
副本机制
Leader副本
Follower副本
ISR列表
客户端
负载均衡器
Broker1
Broker2
Broker3
Topic1-P0
Topic1-P1
Topic1-P2
Topic2-P0
Topic2-P1
Topic2-P2
消息传播方式
1. 发布订阅模式
模式特点
发布订阅模式 :
特点 :
- 一个生产者,多个消费者
- 消息广播到所有消费者
- 消费者独立消费
- 支持消费者组
使用场景 :
- 事件通知
- 日志分发
- 数据同步
- 实时监控
示例 :
- 用户注册事件通知
- 系统告警消息广播
- 配置变更通知
- 数据更新同步
架构图示
发布订阅模式特点
消息广播
消费者独立
支持多组
解耦设计
生产者
Topic: user.events
消费者1: 邮件服务
消费者2: 短信服务
消费者3: 推送服务
消费者4: 日志服务
实际应用场景
@Service
public class UserEventPublisher {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public void publishUserRegistered ( User user) {
UserEvent event = new UserEvent ( ) ;
event. setEventType ( "USER_REGISTERED" ) ;
event. setUserId ( user. getId ( ) ) ;
event. setTimestamp ( System . currentTimeMillis ( ) ) ;
event. setData ( user) ;
kafkaTemplate. send ( "user.events" , user. getId ( ) , JsonUtils . toJson ( event) ) ;
log. info ( "用户注册事件已发布: userId={}" , user. getId ( ) ) ;
}
}
@Component
public class EmailServiceConsumer {
@KafkaListener ( topics = "user.events" , groupId = "email.service" )
public void handleUserEvent ( ConsumerRecord < String , String > record) {
UserEvent event = JsonUtils . fromJson ( record. value ( ) , UserEvent . class ) ;
if ( "USER_REGISTERED" . equals ( event. getEventType ( ) ) ) {
sendWelcomeEmail ( event. getUserId ( ) ) ;
log. info ( "欢迎邮件已发送: userId={}" , event. getUserId ( ) ) ;
}
}
private void sendWelcomeEmail ( String userId) {
}
}
2. 点对点模式
模式特点
点对点模式 :
特点 :
- 一个生产者,一个消费者
- 消息被消费后删除
- 支持负载均衡
- 保证消息顺序
使用场景 :
- 任务分发
- 请求处理
- 数据迁移
- 批量处理
示例 :
- 订单处理任务
- 文件上传处理
- 邮件发送任务
- 报表生成任务
架构图示
点对点模式特点
消费者组内部
负载均衡
顺序保证
任务分发
高可用
消费者1: 处理订单1
消费者2: 处理订单2
消费者3: 处理订单3
生产者
Topic: order.tasks
消费者组: order.processors
实际应用场景
@Service
public class OrderTaskProducer {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public void submitOrderTask ( Order order) {
OrderTask task = new OrderTask ( ) ;
task. setOrderId ( order. getId ( ) ) ;
task. setTaskType ( "PROCESS_ORDER" ) ;
task. setPriority ( order. getPriority ( ) ) ;
task. setTimestamp ( System . currentTimeMillis ( ) ) ;
kafkaTemplate. send ( "order.tasks" , order. getId ( ) , JsonUtils . toJson ( task) ) ;
log. info ( "订单任务已提交: orderId={}" , order. getId ( ) ) ;
}
}
@Component
public class OrderTaskConsumer {
@KafkaListener ( topics = "order.tasks" , groupId = "order.processors" )
public void handleOrderTask ( ConsumerRecord < String , String > record) {
OrderTask task = JsonUtils . fromJson ( record. value ( ) , OrderTask . class ) ;
try {
processOrder ( task) ;
log. info ( "订单任务处理完成: orderId={}" , task. getOrderId ( ) ) ;
} catch ( Exception e) {
log. error ( "订单任务处理失败: orderId={}" , task. getOrderId ( ) , e) ;
}
}
private void processOrder ( OrderTask task) {
log. info ( "开始处理订单: {}" , task. getOrderId ( ) ) ;
Thread . sleep ( 1000 ) ;
}
}
3. 分区策略
策略对比
分区策略 :
轮询策略(Round Robin) :
- 消息均匀分布到分区
- 适合负载均衡场景
- 不保证消息顺序
- 实现简单
哈希策略(Hash) :
- 根据消息键计算分区
- 相同键的消息到同一分区
- 保证消息顺序
- 适合需要顺序的场景
自定义策略 :
- 根据业务逻辑选择分区
- 支持复杂的路由规则
- 灵活性高
- 需要自定义实现
分区策略图示
哈希策略特点
轮询策略特点
轮询策略
哈希策略
自定义策略
键值绑定
顺序保证
业务相关
均匀分布
负载均衡
无顺序保证
消息流
分区策略选择
Round Robin
Hash Based
Custom Strategy
分区0
分区1
分区2
分区0: Key1, Key4
分区1: Key2, Key5
分区2: Key3, Key6
分区0: 高优先级
分区1: 中优先级
分区2: 低优先级
分区策略实现
@Component
public class CustomPartitioner implements Partitioner {
@Override
public int partition ( String topic, Object key, byte [ ] keyBytes,
Object value, byte [ ] valueBytes, Cluster cluster) {
List < PartitionInfo > partitions = cluster. availablePartitionsForTopic ( topic) ;
int numPartitions = partitions. size ( ) ;
if ( keyBytes == null ) {
return Math . abs ( ThreadLocalRandom . current ( ) . nextInt ( ) ) % numPartitions;
}
String keyStr = new String ( keyBytes) ;
if ( keyStr. startsWith ( "HIGH_" ) ) {
return 0 ;
} else if ( keyStr. startsWith ( "MEDIUM_" ) ) {
return 1 ;
} else {
return Math . abs ( keyStr. hashCode ( ) ) % numPartitions;
}
}
@Override
public void close ( ) {
}
@Override
public void configure ( Map < String , ? > configs) {
}
}
@Configuration
public class KafkaProducerConfig {
@Bean
public ProducerFactory < String , String > producerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ProducerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ProducerConfig . KEY_SERIALIZER_CLASS_CONFIG , StringSerializer . class ) ;
configProps. put ( ProducerConfig . VALUE_SERIALIZER_CLASS_CONFIG , StringSerializer . class ) ;
configProps. put ( ProducerConfig . PARTITIONER_CLASS_CONFIG , CustomPartitioner . class ) ;
return new DefaultKafkaProducerFactory < > ( configProps) ;
}
}
4. 消费者组机制
机制特点
消费者组机制 :
负载均衡 :
- 分区在消费者间分配
- 支持动态扩缩容
- 自动故障转移
- 保证消费平衡
容错性 :
- 消费者故障自动处理
- 分区重新分配
- 消费偏移量维护
- 保证消息不丢失
扩展性 :
- 支持添加消费者
- 支持移除消费者
- 自动分区重平衡
- 平滑扩容缩容
消费者组架构图示
graph TB
A[Topic: user.events] --> B[分区0]
A --> C[分区1]
A --> D[分区2]
A --> E[分区3]
subgraph "消费者组: user.processors"
F1[消费者1]
F2[消费者2]
F3[消费者3]
end
B --> F1
C --> F2
D --> F3
E --> F1
subgraph "分区分配策略"
G1[分区0 → 消费者1]
G2[分区1 → 消费者2]
G3[分区2 → 消费者3]
G4[分区3 → 消费者1]
end
F1 -.-> G1
F2 -.-> G2
F3 -.-> G3
F1 -.-> G4
subgraph "消费者组特性"
H1[负载均衡]
H2[故障转移]
H3[动态扩缩容]
H4[偏移量管理]
end
F1 -.-> H1
F2 -.-> H2
F3 -.-> H3
F1 -.-> H4
消费者组实现
@Configuration
public class ConsumerGroupConfig {
@Bean
public ConcurrentKafkaListenerContainerFactory < String , String >
userEventContainerFactory ( ConsumerFactory < String , String > consumerFactory) {
ConcurrentKafkaListenerContainerFactory < String , String > factory =
new ConcurrentKafkaListenerContainerFactory < > ( ) ;
factory. setConsumerFactory ( consumerFactory) ;
factory. setConcurrency ( 3 ) ;
factory. getContainerProperties ( ) . setGroupId ( "user.processors" ) ;
factory. getContainerProperties ( ) . setAckMode ( ContainerProperties. AckMode . MANUAL_IMMEDIATE ) ;
return factory;
}
}
@Component
public class UserEventProcessor {
private static final Logger log = LoggerFactory . getLogger ( UserEventProcessor . class ) ;
@KafkaListener ( topics = "user.events" , groupId = "user.processors" ,
containerFactory = "userEventContainerFactory" )
public void processUserEvent ( ConsumerRecord < String , String > record, Acknowledgment ack) {
try {
String message = record. value ( ) ;
int partition = record. partition ( ) ;
long offset = record. offset ( ) ;
log. info ( "消费者组处理消息: partition={}, offset={}, message={}" ,
partition, offset, message) ;
UserEvent event = JsonUtils . fromJson ( message, UserEvent . class ) ;
handleUserEvent ( event) ;
ack. acknowledge ( ) ;
} catch ( Exception e) {
log. error ( "处理用户事件失败: partition={}, offset={}" ,
record. partition ( ) , record. offset ( ) , e) ;
}
}
private void handleUserEvent ( UserEvent event) {
switch ( event. getEventType ( ) ) {
case "USER_REGISTERED" :
handleUserRegistered ( event) ;
break ;
case "USER_UPDATED" :
handleUserUpdated ( event) ;
break ;
case "USER_DELETED" :
handleUserDeleted ( event) ;
break ;
default :
log. warn ( "未知事件类型: {}" , event. getEventType ( ) ) ;
}
}
private void handleUserRegistered ( UserEvent event) {
log. info ( "处理用户注册事件: userId={}" , event. getUserId ( ) ) ;
}
private void handleUserUpdated ( UserEvent event) {
log. info ( "处理用户更新事件: userId={}" , event. getUserId ( ) ) ;
}
private void handleUserDeleted ( UserEvent event) {
log. info ( "处理用户删除事件: userId={}" , event. getUserId ( ) ) ;
}
}
重难点分析
1. 消息顺序性
问题描述
顺序性挑战 :
问题描述 :
- 多分区并发处理
- 消息可能乱序到达
- 影响业务逻辑正确性
- 难以保证全局顺序
解决方案 :
- 单分区单消费者
- 消息键分区策略
- 业务层排序处理
- 使用时间窗口排序
实现示例 :
- 订单状态变更顺序
- 用户操作日志顺序
- 数据同步顺序
- 事件处理顺序
解决方案代码示例
方案1: 单分区单消费者
@Configuration
public class OrderSequenceConfig {
@Bean
public Queue orderSequenceQueue ( ) {
return QueueBuilder . durable ( "order.sequence.queue" )
. build ( ) ;
}
@Bean
public TopicExchange orderSequenceExchange ( ) {
return new TopicExchange ( "order.sequence.exchange" ) ;
}
@Bean
public Binding orderSequenceBinding ( ) {
return BindingBuilder . bind ( orderSequenceQueue ( ) )
. to ( orderSequenceExchange ( ) )
. with ( "order.sequence.*" ) ;
}
}
@Component
public class OrderSequenceConsumer {
private static final Logger log = LoggerFactory . getLogger ( OrderSequenceConsumer . class ) ;
@RabbitListener ( queues = "#{orderSequenceQueue.name}" )
public void handleOrderSequence ( OrderMessage message) {
try {
log. info ( "处理订单消息: orderId={}, status={}, timestamp={}" ,
message. getOrderId ( ) , message. getStatus ( ) , message. getTimestamp ( ) ) ;
processOrderStatusChange ( message) ;
} catch ( Exception e) {
log. error ( "处理订单消息失败: orderId={}" , message. getOrderId ( ) , e) ;
}
}
private void processOrderStatusChange ( OrderMessage message) {
log. info ( "订单状态变更: {} -> {}" , message. getOrderId ( ) , message. getStatus ( ) ) ;
}
}
方案2: 消息分组路由
@Service
public class OrderMessageRouter {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public void sendOrderMessage ( OrderMessage orderMessage) {
String key = orderMessage. getOrderId ( ) ;
String message = JsonUtils . toJson ( orderMessage) ;
kafkaTemplate. send ( "order.status.topic" , key, message) ;
log. info ( "订单消息已发送: orderId={}, partition={}" ,
key, getPartitionForKey ( key) ) ;
}
private int getPartitionForKey ( String key) {
return Math . abs ( key. hashCode ( ) ) % 3 ;
}
}
@Configuration
public class OrderPartitionConfig {
@Bean
public NewTopic orderStatusTopic ( ) {
return TopicBuilder . name ( "order.status.topic" )
. partitions ( 3 )
. replicas ( 2 )
. build ( ) ;
}
}
方案3: 业务层排序处理
@Component
public class OrderMessageSequencer {
private final Map < String , PriorityBlockingQueue < OrderMessage > > orderQueues =
new ConcurrentHashMap < > ( ) ;
private final ExecutorService executorService = Executors . newFixedThreadPool ( 5 ) ;
public void processOrderMessage ( OrderMessage message) {
String orderId = message. getOrderId ( ) ;
PriorityBlockingQueue < OrderMessage > queue = orderQueues. computeIfAbsent (
orderId, k -> new PriorityBlockingQueue < > ( 100 ,
Comparator . comparing ( OrderMessage :: getTimestamp ) ) ) ;
queue. offer ( message) ;
executorService. submit ( ( ) -> processOrderQueue ( orderId, queue) ) ;
}
private void processOrderQueue ( String orderId, PriorityBlockingQueue < OrderMessage > queue) {
try {
OrderMessage message;
while ( ( message = queue. poll ( 100 , TimeUnit . MILLISECONDS ) ) != null ) {
processOrderMessageSequentially ( message) ;
}
} catch ( InterruptedException e) {
Thread . currentThread ( ) . interrupt ( ) ;
log. warn ( "订单队列处理被中断: orderId={}" , orderId) ;
}
}
private void processOrderMessageSequentially ( OrderMessage message) {
log. info ( "按顺序处理订单消息: orderId={}, status={}, timestamp={}" ,
message. getOrderId ( ) , message. getStatus ( ) , message. getTimestamp ( ) ) ;
}
}
方案4: 使用消息ID关联
@Component
public class OrderMessageCorrelator {
private final Map < String , List < OrderMessage > > messageBuffer = new ConcurrentHashMap < > ( ) ;
private final Map < String , Long > lastProcessedTimestamp = new ConcurrentHashMap < > ( ) ;
public void processOrderMessage ( OrderMessage message) {
String orderId = message. getOrderId ( ) ;
long messageTimestamp = message. getTimestamp ( ) ;
if ( isMessageProcessed ( orderId, messageTimestamp) ) {
log. info ( "消息已处理,跳过: orderId={}, timestamp={}" , orderId, messageTimestamp) ;
return ;
}
messageBuffer. computeIfAbsent ( orderId, k -> new ArrayList < > ( ) ) . add ( message) ;
processOrderMessagesInOrder ( orderId) ;
}
private boolean isMessageProcessed ( String orderId, long timestamp) {
Long lastTimestamp = lastProcessedTimestamp. get ( orderId) ;
return lastTimestamp != null && timestamp <= lastTimestamp;
}
private void processOrderMessagesInOrder ( String orderId) {
List < OrderMessage > messages = messageBuffer. get ( orderId) ;
if ( messages == null || messages. isEmpty ( ) ) {
return ;
}
messages. sort ( Comparator . comparing ( OrderMessage :: getTimestamp ) ) ;
for ( OrderMessage message : messages) {
if ( isMessageProcessed ( orderId, message. getTimestamp ( ) ) ) {
continue ;
}
processOrderMessageSequentially ( message) ;
lastProcessedTimestamp. put ( orderId, message. getTimestamp ( ) ) ;
}
messages. clear ( ) ;
}
private void processOrderMessageSequentially ( OrderMessage message) {
log. info ( "处理订单消息: orderId={}, status={}, timestamp={}" ,
message. getOrderId ( ) , message. getStatus ( ) , message. getTimestamp ( ) ) ;
}
}
2. 消息重复消费
问题描述
重复消费问题 :
产生原因 :
- 消费者重启
- 分区重平衡
- 消费偏移量重置
- 网络异常重试
解决方案 :
- 消息幂等性设计
- 唯一消息ID
- 业务状态检查
- 分布式锁控制
幂等性实现 :
- 数据库唯一约束
- Redis原子操作
- 业务状态机
- 消息去重表
解决方案代码示例
方案1: 消息幂等性设计
@Component
public class IdempotentMessageProcessor {
@Autowired
private RedisTemplate < String , String > redisTemplate;
@Autowired
private OrderService orderService;
public void processMessageIdempotently ( OrderMessage message) {
String messageId = message. getMessageId ( ) ;
String orderId = message. getOrderId ( ) ;
String lockKey = "message:processed:" + messageId;
Boolean acquired = redisTemplate. opsForValue ( ) . setIfAbsent ( lockKey, "1" , 24 , TimeUnit . HOURS ) ;
if ( Boolean . TRUE . equals ( acquired) ) {
try {
processOrderMessage ( message) ;
log. info ( "消息处理成功: messageId={}, orderId={}" , messageId, orderId) ;
} catch ( Exception e) {
log. error ( "消息处理失败: messageId={}, orderId={}" , messageId, orderId, e) ;
redisTemplate. delete ( lockKey) ;
throw e;
}
} else {
log. info ( "消息已处理,跳过: messageId={}, orderId={}" , messageId, orderId) ;
}
}
private void processOrderMessage ( OrderMessage message) {
orderService. updateOrderStatus ( message. getOrderId ( ) , message. getStatus ( ) ) ;
}
}
方案2: 唯一消息ID
@Component
public class MessageIdGenerator {
public String generateMessageId ( String businessKey, String operation) {
String timestamp = String . valueOf ( System . currentTimeMillis ( ) ) ;
String uuid = UUID . randomUUID ( ) . toString ( ) . replace ( "-" , "" ) ;
return String . format ( "%s_%s_%s_%s" , businessKey, operation, timestamp, uuid) ;
}
public String generateSignature ( String messageId, String content) {
try {
MessageDigest digest = MessageDigest . getInstance ( "SHA-256" ) ;
byte [ ] hash = digest. digest ( ( messageId + content) . getBytes ( StandardCharsets . UTF_8 ) ) ;
return Base64 . getEncoder ( ) . encodeToString ( hash) ;
} catch ( NoSuchAlgorithmException e) {
throw new RuntimeException ( "生成消息签名失败" , e) ;
}
}
public boolean verifySignature ( String messageId, String content, String signature) {
String expectedSignature = generateSignature ( messageId, content) ;
return expectedSignature. equals ( signature) ;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IdempotentMessage {
private String messageId;
private String businessKey;
private String operation;
private String content;
private String signature;
private long timestamp;
private int retryCount;
}
方案3: 业务状态检查
@Service
public class BusinessStateChecker {
@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentRepository paymentRepository;
public boolean canProcessOrderMessage ( OrderMessage message) {
String orderId = message. getOrderId ( ) ;
String targetStatus = message. getStatus ( ) ;
Order order = orderRepository. findById ( orderId) . orElse ( null ) ;
if ( order == null ) {
log. warn ( "订单不存在: orderId={}" , orderId) ;
return false ;
}
return isValidStatusTransition ( order. getStatus ( ) , targetStatus) ;
}
public boolean canProcessPayment ( PaymentMessage message) {
String paymentId = message. getPaymentId ( ) ;
Payment payment = paymentRepository. findById ( paymentId) . orElse ( null ) ;
if ( payment == null ) {
log. warn ( "支付记录不存在: paymentId={}" , paymentId) ;
return false ;
}
return "PENDING" . equals ( payment. getStatus ( ) ) ;
}
public boolean canProcessShipping ( ShippingMessage message) {
String orderId = message. getOrderId ( ) ;
Order order = orderRepository. findById ( orderId) . orElse ( null ) ;
if ( order == null ) {
return false ;
}
return "PAID" . equals ( order. getStatus ( ) ) ;
}
public boolean canProcessCompletion ( CompletionMessage message) {
String orderId = message. getOrderId ( ) ;
Order order = orderRepository. findById ( orderId) . orElse ( null ) ;
if ( order == null ) {
return false ;
}
return "SHIPPED" . equals ( order. getStatus ( ) ) ;
}
private boolean isValidStatusTransition ( String currentStatus, String targetStatus) {
Map < String , Set < String > > validTransitions = Map . of (
"CREATED" , Set . of ( "PAID" , "CANCELLED" ) ,
"PAID" , Set . of ( "SHIPPED" , "REFUNDED" ) ,
"SHIPPED" , Set . of ( "DELIVERED" , "RETURNED" ) ,
"DELIVERED" , Set . of ( "COMPLETED" , "RETURNED" )
) ;
Set < String > allowedTargets = validTransitions. get ( currentStatus) ;
return allowedTargets != null && allowedTargets. contains ( targetStatus) ;
}
}
方案4: 分布式锁控制
@Component
public class DistributedLockProcessor {
@Autowired
private RedisTemplate < String , String > redisTemplate;
@Autowired
private OrderService orderService;
private static final long LOCK_TIMEOUT = 30000 ;
private static final long WAIT_TIMEOUT = 10000 ;
public void processMessageWithLock ( OrderMessage message) {
String lockKey = "order:lock:" + message. getOrderId ( ) ;
String lockValue = UUID . randomUUID ( ) . toString ( ) ;
try {
if ( acquireLock ( lockKey, lockValue, LOCK_TIMEOUT ) ) {
try {
processOrderMessage ( message) ;
log. info ( "消息处理成功: orderId={}" , message. getOrderId ( ) ) ;
} finally {
releaseLock ( lockKey, lockValue) ;
}
} else {
waitAndRetry ( message, lockKey, lockValue) ;
}
} catch ( Exception e) {
log. error ( "消息处理失败: orderId={}" , message. getOrderId ( ) , e) ;
releaseLock ( lockKey, lockValue) ;
throw e;
}
}
private boolean acquireLock ( String lockKey, String lockValue, long timeout) {
Boolean acquired = redisTemplate. opsForValue ( ) . setIfAbsent ( lockKey, lockValue, timeout, TimeUnit . MILLISECONDS ) ;
return Boolean . TRUE . equals ( acquired) ;
}
private void releaseLock ( String lockKey, String lockValue) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" ;
redisTemplate. execute ( new DefaultRedisScript < > ( script, Long . class ) ,
Collections . singletonList ( lockKey) , lockValue) ;
}
private void waitAndRetry ( OrderMessage message, String lockKey, String lockValue) {
long startTime = System . currentTimeMillis ( ) ;
while ( System . currentTimeMillis ( ) - startTime < WAIT_TIMEOUT ) {
try {
Thread . sleep ( 100 ) ;
if ( acquireLock ( lockKey, lockValue, LOCK_TIMEOUT ) ) {
try {
processOrderMessage ( message) ;
log. info ( "重试后消息处理成功: orderId={}" , message. getOrderId ( ) ) ;
return ;
} finally {
releaseLock ( lockKey, lockValue) ;
}
}
} catch ( InterruptedException e) {
Thread . currentThread ( ) . interrupt ( ) ;
break ;
}
}
log. warn ( "获取锁超时,消息处理失败: orderId={}" , message. getOrderId ( ) ) ;
}
private void processOrderMessage ( OrderMessage message) {
orderService. updateOrderStatus ( message. getOrderId ( ) , message. getStatus ( ) ) ;
}
}
3. 分区重平衡
问题描述
分区重平衡机制 :
触发条件 :
- 消费者加入
- 消费者离开
- 消费者崩溃
- 分区数量变化
影响 :
- 消费暂停
- 分区重新分配
- 消费偏移量重置
- 性能下降
优化策略 :
- 减少重平衡频率
- 使用静态成员ID
- 合理设置会话超时
- 监控重平衡事件
解决方案代码示例
方案1: 减少重平衡频率
@Configuration
public class RebalanceOptimizationConfig {
@Bean
public ConsumerFactory < String , String > consumerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ConsumerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ConsumerConfig . GROUP_ID_CONFIG , "optimized.group" ) ;
configProps. put ( ConsumerConfig . KEY_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
configProps. put ( ConsumerConfig . VALUE_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
configProps. put ( ConsumerConfig . SESSION_TIMEOUT_MS_CONFIG , 45000 ) ;
configProps. put ( ConsumerConfig . HEARTBEAT_INTERVAL_MS_CONFIG , 15000 ) ;
configProps. put ( ConsumerConfig . MAX_POLL_INTERVAL_MS_CONFIG , 300000 ) ;
configProps. put ( ConsumerConfig . MAX_POLL_RECORDS_CONFIG , 500 ) ;
return new DefaultKafkaConsumerFactory < > ( configProps) ;
}
@Bean
public ConcurrentKafkaListenerContainerFactory < String , String > kafkaListenerContainerFactory ( ) {
ConcurrentKafkaListenerContainerFactory < String , String > factory =
new ConcurrentKafkaListenerContainerFactory < > ( ) ;
factory. setConsumerFactory ( consumerFactory ( ) ) ;
factory. setConcurrency ( 3 ) ;
factory. getContainerProperties ( ) . setConsumerRebalanceListener ( new ConsumerAwareRebalanceListener ( ) {
@Override
public void onPartitionsRevokedBeforeCommit ( Consumer < ? , ? > consumer, Collection < TopicPartition > partitions) {
log. info ( "分区被撤销: partitions={}" , partitions) ;
consumer. commitSync ( ) ;
}
@Override
public void onPartitionsAssigned ( Collection < TopicPartition > partitions) {
log. info ( "分区被分配: partitions={}" , partitions) ;
}
} ) ;
return factory;
}
}
方案2: 使用静态成员ID
@Configuration
public class StaticMemberConfig {
@Value ( "${spring.application.name:unknown}" )
private String applicationName;
@Value ( "${server.port:8080}" )
private String serverPort;
@Bean
public ConsumerFactory < String , String > consumerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ConsumerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ConsumerConfig . GROUP_ID_CONFIG , "static.group" ) ;
String staticMemberId = applicationName + "-" + serverPort + "-" +
InetAddress . getLocalHost ( ) . getHostAddress ( ) ;
configProps. put ( ConsumerConfig . GROUP_INSTANCE_ID_CONFIG , staticMemberId) ;
configProps. put ( ConsumerConfig . KEY_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
configProps. put ( ConsumerConfig . VALUE_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
return new DefaultKafkaConsumerFactory < > ( configProps) ;
}
}
方案3: 合理设置会话超时
@Configuration
public class SessionTimeoutConfig {
@Bean
public ConsumerFactory < String , String > consumerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ConsumerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ConsumerConfig . GROUP_ID_CONFIG , "timeout.optimized.group" ) ;
configProps. put ( ConsumerConfig . SESSION_TIMEOUT_MS_CONFIG , 30000 ) ;
configProps. put ( ConsumerConfig . HEARTBEAT_INTERVAL_MS_CONFIG , 10000 ) ;
configProps. put ( ConsumerConfig . MAX_POLL_INTERVAL_MS_CONFIG , 300000 ) ;
configProps. put ( ConsumerConfig . KEY_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
configProps. put ( ConsumerConfig . VALUE_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
return new DefaultKafkaConsumerFactory < > ( configProps) ;
}
}
方案4: 监控重平衡事件
@Component
public class RebalanceEventMonitor {
private static final Logger log = LoggerFactory . getLogger ( RebalanceEventMonitor . class ) ;
private final AtomicLong rebalanceCount = new AtomicLong ( 0 ) ;
private final AtomicLong lastRebalanceTime = new AtomicLong ( 0 ) ;
private final Map < String , Long > partitionAssignmentHistory = new ConcurrentHashMap < > ( ) ;
@EventListener
public void handleRebalanceEvent ( ConsumerRebalanceEvent event) {
long currentTime = System . currentTimeMillis ( ) ;
long count = rebalanceCount. incrementAndGet ( ) ;
long lastTime = lastRebalanceTime. get ( ) ;
log. info ( "重平衡事件发生: type={}, count={}, lastRebalance={}ms ago" ,
event. getType ( ) , count, lastTime > 0 ? currentTime - lastTime : 0 ) ;
lastRebalanceTime. set ( currentTime) ;
if ( event. getPartitions ( ) != null ) {
for ( TopicPartition partition : event. getPartitions ( ) ) {
String key = partition. topic ( ) + "-" + partition. partition ( ) ;
partitionAssignmentHistory. put ( key, currentTime) ;
}
}
sendRebalanceMetrics ( event, count, currentTime - lastTime) ;
}
private void sendRebalanceMetrics ( ConsumerRebalanceEvent event, long count, long interval) {
log. info ( "重平衡监控指标: type={}, totalCount={}, interval={}ms" ,
event. getType ( ) , count, interval) ;
}
public Map < String , Object > getRebalanceStats ( ) {
Map < String , Object > stats = new HashMap < > ( ) ;
stats. put ( "totalRebalanceCount" , rebalanceCount. get ( ) ) ;
stats. put ( "lastRebalanceTime" , lastRebalanceTime. get ( ) ) ;
stats. put ( "partitionAssignmentHistory" , new HashMap < > ( partitionAssignmentHistory) ) ;
return stats;
}
}
4. 数据一致性
问题描述
数据一致性挑战 :
副本同步 :
- 主从副本同步延迟
- 网络分区影响
- 副本故障处理
- 数据一致性保证
解决方案 :
- 同步复制配置
- 最小副本数设置
- 一致性级别配置
- 监控副本状态
配置要点 :
- acks参数设置
- min.insync.replicas配置
- 副本因子设置
- 同步复制策略
解决方案代码示例
方案1: 同步复制配置
@Configuration
public class ProducerConsistencyConfig {
@Bean
public ProducerFactory < String , String > producerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ProducerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ProducerConfig . KEY_SERIALIZER_CLASS_CONFIG , StringSerializer . class ) ;
configProps. put ( ProducerConfig . VALUE_SERIALIZER_CLASS_CONFIG , StringSerializer . class ) ;
configProps. put ( ProducerConfig . ACKS_CONFIG , "all" ) ;
configProps. put ( ProducerConfig . RETRIES_CONFIG , 3 ) ;
configProps. put ( ProducerConfig . RETRY_BACKOFF_MS_CONFIG , 1000 ) ;
configProps. put ( ProducerConfig . REQUEST_TIMEOUT_MS_CONFIG , 30000 ) ;
return new DefaultKafkaProducerFactory < > ( configProps) ;
}
@Bean
public KafkaTemplate < String , String > kafkaTemplate ( ) {
return new KafkaTemplate < > ( producerFactory ( ) ) ;
}
}
@Service
public class ConsistentMessageSender {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public CompletableFuture < SendResult < String , String > > sendMessageConsistently (
String topic, String key, String message) {
ProducerRecord < String , String > record = new ProducerRecord < > ( topic, key, message) ;
return kafkaTemplate. send ( record)
. completable ( )
. whenComplete ( ( result, throwable) -> {
if ( throwable != null ) {
log. error ( "消息发送失败: topic={}, key={}" , topic, key, throwable) ;
} else {
log. info ( "消息发送成功: topic={}, partition={}, offset={}" ,
result. getRecordMetadata ( ) . topic ( ) ,
result. getRecordMetadata ( ) . partition ( ) ,
result. getRecordMetadata ( ) . offset ( ) ) ;
}
} ) ;
}
}
方案2: 最小副本数设置
@Configuration
public class TopicConsistencyConfig {
@Bean
public NewTopic consistentTopic ( ) {
return TopicBuilder . name ( "consistent.topic" )
. partitions ( 3 )
. replicas ( 3 )
. configs ( Map . of (
"min.insync.replicas" , "2" ,
"cleanup.policy" , "delete" ,
"retention.ms" , "604800000"
) )
. build ( ) ;
}
@Bean
public NewTopic highConsistencyTopic ( ) {
return TopicBuilder . name ( "high.consistency.topic" )
. partitions ( 5 )
. replicas ( 5 )
. configs ( Map . of (
"min.insync.replicas" , "4" ,
"cleanup.policy" , "delete" ,
"retention.ms" , "2592000000"
) )
. build ( ) ;
}
}
方案3: 一致性级别配置
@Service
public class ConsistencyLevelProducer {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public void sendWithHighestConsistency ( String topic, String key, String message) {
Map < String , Object > configs = new HashMap < > ( ) ;
configs. put ( ProducerConfig . ACKS_CONFIG , "all" ) ;
configs. put ( ProducerConfig . ENABLE_IDEMPOTENCE_CONFIG , true ) ;
configs. put ( ProducerConfig . MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION , 1 ) ;
KafkaTemplate < String , String > template = new KafkaTemplate < > (
new DefaultKafkaProducerFactory < > ( configs) ) ;
template. send ( topic, key, message) ;
log. info ( "高一致性消息已发送: topic={}, key={}" , topic, key) ;
}
public void sendWithMediumConsistency ( String topic, String key, String message) {
Map < String , Object > configs = new HashMap < > ( ) ;
configs. put ( ProducerConfig . ACKS_CONFIG , "1" ) ;
configs. put ( ProducerConfig . RETRIES_CONFIG , 3 ) ;
KafkaTemplate < String , String > template = new KafkaTemplate < > (
new DefaultKafkaProducerFactory < > ( configs) ) ;
template. send ( topic, key, message) ;
log. info ( "中等一致性消息已发送: topic={}, key={}" , topic, key) ;
}
public void sendWithLowestConsistency ( String topic, String key, String message) {
Map < String , Object > configs = new HashMap < > ( ) ;
configs. put ( ProducerConfig . ACKS_CONFIG , "0" ) ;
configs. put ( ProducerConfig . RETRIES_CONFIG , 0 ) ;
KafkaTemplate < String , String > template = new KafkaTemplate < > (
new DefaultKafkaProducerFactory < > ( configs) ) ;
template. send ( topic, key, message) ;
log. info ( "低一致性消息已发送: topic={}, key={}" , topic, key) ;
}
}
方案4: 监控副本状态
@Component
public class ReplicaStatusMonitor {
private static final Logger log = LoggerFactory . getLogger ( ReplicaStatusMonitor . class ) ;
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
@Scheduled ( fixedRate = 30000 )
public void checkReplicaStatus ( ) {
try {
Cluster cluster = getClusterMetadata ( ) ;
for ( String topic : getTopics ( ) ) {
checkTopicReplicaStatus ( topic, cluster) ;
}
} catch ( Exception e) {
log. error ( "检查副本状态失败" , e) ;
}
}
private void checkTopicReplicaStatus ( String topic, Cluster cluster) {
List < PartitionInfo > partitions = cluster. partitionsForTopic ( topic) ;
for ( PartitionInfo partition : partitions) {
int replicaCount = partition. replicas ( ) . length;
int inSyncReplicaCount = partition. inSyncReplicas ( ) . length;
int offlineReplicaCount = partition. offlineReplicas ( ) . length;
log. info ( "Topic: {}, Partition: {}, Replicas: {}/{}, Offline: {}" ,
topic, partition. partition ( ) , inSyncReplicaCount, replicaCount, offlineReplicaCount) ;
if ( inSyncReplicaCount < replicaCount) {
log. warn ( "Topic: {}, Partition: {} 副本同步异常" , topic, partition. partition ( ) ) ;
sendReplicaAlert ( topic, partition. partition ( ) , inSyncReplicaCount, replicaCount) ;
}
if ( offlineReplicaCount > 0 ) {
log. error ( "Topic: {}, Partition: {} 有离线副本" , topic, partition. partition ( ) ) ;
sendReplicaAlert ( topic, partition. partition ( ) , offlineReplicaCount, replicaCount) ;
}
}
}
private void sendReplicaAlert ( String topic, int partition, int current, int expected) {
String alertMessage = String . format ( "副本状态异常: Topic=%s, Partition=%d, Current=%d, Expected=%d" ,
topic, partition, current, expected) ;
log. warn ( alertMessage) ;
}
private Cluster getClusterMetadata ( ) {
return null ;
}
private List < String > getTopics ( ) {
return Collections . emptyList ( ) ;
}
}
消息延迟处理
1. 延迟队列实现方式
时间轮算法
时间轮算法 :
实现原理 :
- 使用环形数组存储延迟任务
- 按时间粒度分槽
- 支持毫秒级精度
- 高效的任务调度
优点 :
- 时间复杂度O(1)
- 内存占用小
- 支持大量延迟任务
- 精度高
缺点 :
- 实现复杂
- 时间粒度固定
- 不适合动态调整
- 学习成本高
适用场景 :
- 高精度延迟需求
- 大量延迟任务
- 性能要求高
- 生产环境使用
分层时间轮
分层时间轮 :
实现原理 :
- 多层时间轮组合
- 不同层级不同精度
- 支持大范围延迟时间
- 自动任务迁移
优点 :
- 支持大范围延迟
- 内存效率高
- 自动任务迁移
- 精度可配置
缺点 :
- 实现更复杂
- 任务迁移开销
- 调试困难
- 维护成本高
适用场景 :
- 大范围延迟时间
- 高精度要求
- 内存受限环境
- 企业级应用
2. 延迟队列应用场景
应用场景 :
订单超时 :
- 下单后15分钟未支付
- 自动取消订单
- 释放库存
- 发送超时通知
任务调度 :
- 定时任务执行
- 延迟任务处理
- 周期性任务
- 批量任务调度
重试机制 :
- 失败重试
- 指数退避
- 最大重试次数
- 重试策略配置
业务提醒 :
- 预约提醒
- 到期通知
- 定时推送
- 周期性提醒
3. 延迟队列实现示例
@Component
public class TimeWheelDelayQueue {
private final Timer timer;
private final Map < String , TimerTask > taskMap = new ConcurrentHashMap < > ( ) ;
public TimeWheelDelayQueue ( ) {
this . timer = new HashedWheelTimer ( 1 , TimeUnit . MILLISECONDS , 512 ) ;
}
public void addDelayTask ( String taskId, Runnable task, long delayMillis) {
TimerTask timerTask = new TimerTask ( ) {
@Override
public void run ( Timeout timeout) {
try {
task. run ( ) ;
taskMap. remove ( taskId) ;
} catch ( Exception e) {
log. error ( "延迟任务执行失败: {}" , taskId, e) ;
}
}
} ;
taskMap. put ( taskId, timerTask) ;
timer. newTimeout ( timerTask, delayMillis, TimeUnit . MILLISECONDS ) ;
log. info ( "延迟任务已添加: {}, 延迟时间: {}ms" , taskId, delayMillis) ;
}
public boolean cancelDelayTask ( String taskId) {
TimerTask timerTask = taskMap. get ( taskId) ;
if ( timerTask != null ) {
timerTask. cancel ( ) ;
taskMap. remove ( taskId) ;
log. info ( "延迟任务已取消: {}" , taskId) ;
return true ;
}
return false ;
}
public int getTaskCount ( ) {
return taskMap. size ( ) ;
}
public void shutdown ( ) {
timer. stop ( ) ;
log. info ( "时间轮已关闭" ) ;
}
}
@Component
public class HierarchicalTimeWheel {
private final List < TimeWheel > timeWheels;
private final ExecutorService executorService;
public HierarchicalTimeWheel ( ) {
this . timeWheels = new ArrayList < > ( ) ;
this . executorService = Executors . newFixedThreadPool ( 4 ) ;
timeWheels. add ( new TimeWheel ( 1 , 1000 , 1000 ) ) ;
timeWheels. add ( new TimeWheel ( 1000 , 60 , 60 ) ) ;
timeWheels. add ( new TimeWheel ( 60000 , 60 , 60 ) ) ;
timeWheels. add ( new TimeWheel ( 3600000 , 24 , 24 ) ) ;
}
public void addTask ( DelayTask task) {
TimeWheel timeWheel = selectTimeWheel ( task. getDelayMillis ( ) ) ;
timeWheel. addTask ( task) ;
}
private TimeWheel selectTimeWheel ( long delayMillis) {
for ( TimeWheel timeWheel : timeWheels) {
if ( delayMillis <= timeWheel. getMaxDelay ( ) ) {
return timeWheel;
}
}
return timeWheels. get ( timeWheels. size ( ) - 1 ) ;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DelayTask {
private String taskId;
private String topic;
private String key;
private Object data;
private long delayMillis;
private long createTime;
private int retryCount;
private String status;
}
消息丢失处理方案
1. 消息丢失场景分析
丢失场景 :
生产者丢失 :
- 网络异常
- 服务崩溃
- 配置错误
- 分区不可用
Broker丢失 :
- 磁盘故障
- 内存不足
- 服务重启
- 副本同步失败
消费者丢失 :
- 处理异常
- 服务重启
- 偏移量重置
- 分区重平衡
2. 生产者消息可靠性
生产者可靠性 :
acks配置 :
- acks=0 : 不等待确认,性能最高
- acks=1 : 等待Leader确认
- acks=all : 等待所有副本确认
重试机制 :
- 自动重试
- 指数退避
- 最大重试次数
- 重试间隔配置
异常处理 :
- 网络异常重试
- 分区不可用处理
- 超时处理
- 错误回调
3. Broker消息可靠性
Broker可靠性 :
副本配置 :
- 副本因子设置
- 最小同步副本数
- 副本分布策略
- 自动故障转移
持久化配置 :
- 消息刷盘策略
- 副本同步策略
- 数据压缩配置
- 清理策略配置
监控告警 :
- 副本状态监控
- 同步延迟监控
- 磁盘空间监控
- 性能指标监控
4. 消费者消息可靠性
消费者可靠性 :
偏移量管理 :
- 自动提交配置
- 手动提交策略
- 偏移量重置策略
- 偏移量存储位置
异常处理 :
- 业务异常处理
- 系统异常处理
- 重试机制
- 死信队列
幂等性保证 :
- 唯一标识
- 状态检查
- 分布式锁
- 业务逻辑设计
5. 完整可靠性方案
@Component
public class ReliableKafkaProducer {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public void sendMessageReliably ( String topic, String key, String message) {
ProducerRecord < String , String > record = new ProducerRecord < > ( topic, key, message) ;
kafkaTemplate. send ( record)
. addCallback (
result -> {
log. info ( "消息发送成功: topic={}, partition={}, offset={}" ,
result. getRecordMetadata ( ) . topic ( ) ,
result. getRecordMetadata ( ) . partition ( ) ,
result. getRecordMetadata ( ) . offset ( ) ) ;
} ,
ex -> {
log. error ( "消息发送失败: topic={}, key={}" , topic, key, ex) ;
handleSendFailure ( topic, key, message, ex) ;
}
) ;
}
private void handleSendFailure ( String topic, String key, String message, Exception ex) {
log. info ( "开始重试发送消息: topic={}, key={}" , topic, key) ;
retrySendMessage ( topic, key, message) ;
}
private void retrySendMessage ( String topic, String key, String message) {
try {
Thread . sleep ( 1000 ) ;
kafkaTemplate. send ( topic, key, message) ;
} catch ( Exception e) {
log. error ( "重试发送失败: topic={}, key={}" , topic, key, e) ;
}
}
}
@Component
public class ReliableKafkaConsumer {
private static final Logger log = LoggerFactory . getLogger ( ReliableKafkaConsumer . class ) ;
@KafkaListener ( topics = "reliable.topic" , groupId = "reliable.group" )
public void handleMessage ( ConsumerRecord < String , String > record, Acknowledgment ack) {
try {
processMessage ( record) ;
ack. acknowledge ( ) ;
log. info ( "消息处理成功: topic={}, partition={}, offset={}" ,
record. topic ( ) , record. partition ( ) , record. offset ( ) ) ;
} catch ( Exception e) {
log. error ( "消息处理失败: topic={}, partition={}, offset={}" ,
record. topic ( ) , record. partition ( ) , record. offset ( ) , e) ;
if ( isRecoverableException ( e) ) {
log. info ( "可恢复异常,消息将重试" ) ;
} else {
log. warn ( "不可恢复异常,确认消息" ) ;
ack. acknowledge ( ) ;
}
}
}
private void processMessage ( ConsumerRecord < String , String > record) {
String message = record. value ( ) ;
log. info ( "处理消息: {}" , message) ;
}
private boolean isRecoverableException ( Exception e) {
return e instanceof IOException ||
e instanceof TimeoutException ||
e instanceof NetworkException ;
}
}
与其他消息队列比较
1. 功能特性对比
特性 Kafka RabbitMQ RocketMQ ActiveMQ 协议 自定义协议 AMQP 自定义协议 JMS 消息模式 发布订阅 支持多种模式 支持多种模式 支持多种模式 消息顺序 分区内保证 单队列保证 支持 支持 消息持久化 支持 支持 支持 支持 集群模式 支持 支持 支持 支持 管理界面 基础 完善 基础 基础 学习曲线 较高 中等 中等 较低
2. 性能对比
性能指标 Kafka RabbitMQ RocketMQ ActiveMQ 吞吐量 最高<br>适合大数据场景 中等<br>适合一般业务场景 较高<br>适合高并发场景 较低<br>适合传统企业场景 延迟 中等<br>适合批处理场景 最低<br>适合实时场景 较低<br>适合一般场景 较高<br>适合非实时场景 可靠性 中等<br>支持至少一次语义 最高<br>支持多种确认机制 较高<br>支持事务消息 较高<br>支持JMS规范
3. 适用场景对比
适用场景 Kafka RabbitMQ RocketMQ ActiveMQ 主要应用 大数据流处理<br>日志收集 传统企业应用<br>微服务架构 金融业务<br>电商系统 传统JMS应用<br>企业集成 核心优势 高吞吐量<br>流处理能力 灵活路由<br>可靠性高 事务消息<br>顺序消息 JMS规范<br>成熟稳定 技术特点 分区机制<br>流式处理 多种交换机<br>完善集群 事务消息<br>高并发 JMS规范<br>企业级
集成SpringBoot应用
1. 基础配置
Maven依赖
< dependency>
< groupId> org.springframework.kafka</ groupId>
< artifactId> spring-kafka</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
配置文件
spring :
kafka :
bootstrap-servers : localhost: 9092
producer :
key-serializer : org.apache.kafka.common.serialization.StringSerializer
value-serializer : org.apache.kafka.common.serialization.StringSerializer
acks : all
retries : 3
batch-size : 16384
linger-ms : 1
buffer-memory : 33554432
consumer :
group-id : my- group
auto-offset-reset : earliest
key-deserializer : org.apache.kafka.common.serialization.StringDeserializer
value-deserializer : org.apache.kafka.common.serialization.StringDeserializer
enable-auto-commit : false
auto-commit-interval : 1000
session-timeout-ms : 30000
max-poll-records : 500
2. 配置类
@Configuration
@EnableKafka
public class KafkaConfig {
@Bean
public ProducerFactory < String , String > producerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ProducerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ProducerConfig . KEY_SERIALIZER_CLASS_CONFIG , StringSerializer . class ) ;
configProps. put ( ProducerConfig . VALUE_SERIALIZER_CLASS_CONFIG , StringSerializer . class ) ;
configProps. put ( ProducerConfig . ACKS_CONFIG , "all" ) ;
configProps. put ( ProducerConfig . RETRIES_CONFIG , 3 ) ;
configProps. put ( ProducerConfig . BATCH_SIZE_CONFIG , 16384 ) ;
configProps. put ( ProducerConfig . LINGER_MS_CONFIG , 1 ) ;
configProps. put ( ProducerConfig . BUFFER_MEMORY_CONFIG , 33554432 ) ;
return new DefaultKafkaProducerFactory < > ( configProps) ;
}
@Bean
public KafkaTemplate < String , String > kafkaTemplate ( ) {
return new KafkaTemplate < > ( producerFactory ( ) ) ;
}
@Bean
public ConsumerFactory < String , String > consumerFactory ( ) {
Map < String , Object > configProps = new HashMap < > ( ) ;
configProps. put ( ConsumerConfig . BOOTSTRAP_SERVERS_CONFIG , "localhost:9092" ) ;
configProps. put ( ConsumerConfig . GROUP_ID_CONFIG , "my-group" ) ;
configProps. put ( ConsumerConfig . KEY_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
configProps. put ( ConsumerConfig . VALUE_DESERIALIZER_CLASS_CONFIG , StringDeserializer . class ) ;
configProps. put ( ConsumerConfig . AUTO_OFFSET_RESET_CONFIG , "earliest" ) ;
configProps. put ( ConsumerConfig . ENABLE_AUTO_COMMIT_CONFIG , false ) ;
return new DefaultKafkaConsumerFactory < > ( configProps) ;
}
@Bean
public ConcurrentKafkaListenerContainerFactory < String , String > kafkaListenerContainerFactory ( ) {
ConcurrentKafkaListenerContainerFactory < String , String > factory =
new ConcurrentKafkaListenerContainerFactory < > ( ) ;
factory. setConsumerFactory ( consumerFactory ( ) ) ;
factory. setConcurrency ( 3 ) ;
factory. getContainerProperties ( ) . setAckMode ( ContainerProperties. AckMode . MANUAL_IMMEDIATE ) ;
return factory;
}
}
3. 生产者实现
@Service
public class KafkaMessageProducer {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
public void sendMessage ( String topic, String message) {
kafkaTemplate. send ( topic, message) ;
log. info ( "消息已发送: topic={}, message={}" , topic, message) ;
}
public void sendMessage ( String topic, String key, String message) {
kafkaTemplate. send ( topic, key, message) ;
log. info ( "消息已发送: topic={}, key={}, message={}" , topic, key, message) ;
}
public void sendMessageWithCallback ( String topic, String key, String message) {
kafkaTemplate. send ( topic, key, message)
. addCallback (
result -> {
log. info ( "消息发送成功: topic={}, partition={}, offset={}" ,
result. getRecordMetadata ( ) . topic ( ) ,
result. getRecordMetadata ( ) . partition ( ) ,
result. getRecordMetadata ( ) . offset ( ) ) ;
} ,
ex -> {
log. error ( "消息发送失败: topic={}, key={}" , topic, key, ex) ;
}
) ;
}
public void sendBatchMessages ( String topic, List < String > messages) {
for ( String message : messages) {
kafkaTemplate. send ( topic, message) ;
}
log. info ( "批量消息已发送: topic={}, count={}" , topic, messages. size ( ) ) ;
}
}
4. 消费者实现
@Component
public class KafkaMessageConsumer {
private static final Logger log = LoggerFactory . getLogger ( KafkaMessageConsumer . class ) ;
@KafkaListener ( topics = "user.topic" , groupId = "user.group" )
public void handleUserMessage ( ConsumerRecord < String , String > record, Acknowledgment ack) {
try {
String message = record. value ( ) ;
String key = record. key ( ) ;
log. info ( "收到用户消息: topic={}, partition={}, offset={}, key={}, value={}" ,
record. topic ( ) , record. partition ( ) , record. offset ( ) , key, message) ;
processUserMessage ( message) ;
ack. acknowledge ( ) ;
} catch ( Exception e) {
log. error ( "处理用户消息失败: topic={}, partition={}, offset={}" ,
record. topic ( ) , record. partition ( ) , record. offset ( ) , e) ;
if ( isRecoverableException ( e) ) {
log. info ( "可恢复异常,消息将重试" ) ;
} else {
log. warn ( "不可恢复异常,确认消息" ) ;
ack. acknowledge ( ) ;
}
}
}
@KafkaListener ( topics = { "order.topic" , "payment.topic" } , groupId = "business.group" )
public void handleBusinessMessage ( ConsumerRecord < String , String > record, Acknowledgment ack) {
try {
String message = record. value ( ) ;
String topic = record. topic ( ) ;
log. info ( "收到业务消息: topic={}, partition={}, offset={}, value={}" ,
topic, record. partition ( ) , record. offset ( ) , message) ;
switch ( topic) {
case "order.topic" :
processOrderMessage ( message) ;
break ;
case "payment.topic" :
processPaymentMessage ( message) ;
break ;
default :
log. warn ( "未知主题: {}" , topic) ;
}
ack. acknowledge ( ) ;
} catch ( Exception e) {
log. error ( "处理业务消息失败: topic={}, partition={}, offset={}" ,
record. topic ( ) , record. partition ( ) , record. offset ( ) , e) ;
ack. acknowledge ( ) ;
}
}
private void processUserMessage ( String message) {
log. info ( "处理用户消息: {}" , message) ;
}
private void processOrderMessage ( String message) {
log. info ( "处理订单消息: {}" , message) ;
}
private void processPaymentMessage ( String message) {
log. info ( "处理支付消息: {}" , message) ;
}
private boolean isRecoverableException ( Exception e) {
return e instanceof IOException ||
e instanceof TimeoutException ||
e instanceof NetworkException ;
}
}
5. 消息模型
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserMessage {
private String userId;
private String eventType;
private long timestamp;
private Object data;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderMessage {
private String orderId;
private String userId;
private String status;
private BigDecimal amount;
private long timestamp;
private Object data;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PaymentMessage {
private String paymentId;
private String orderId;
private String status;
private BigDecimal amount;
private long timestamp;
private Object data;
}
6. 异常处理和重试
@Component
public class KafkaRetryHandler {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
private static final int MAX_RETRY_COUNT = 3 ;
private static final long [ ] RETRY_DELAYS = { 1000 , 5000 , 15000 } ;
public void handleFailedMessage ( String topic, String key, String message, Exception ex, int retryCount) {
if ( retryCount < MAX_RETRY_COUNT ) {
long delay = RETRY_DELAYS [ Math . min ( retryCount, RETRY_DELAYS . length - 1 ) ] ;
log. info ( "准备重试消息: topic={}, key={}, retryCount={}, delay={}ms" ,
topic, key, retryCount + 1 , delay) ;
scheduleRetry ( topic, key, message, retryCount + 1 , delay) ;
} else {
log. warn ( "消息重试次数超过限制,发送到死信队列: topic={}, key={}" , topic, key) ;
sendToDeadLetterQueue ( topic, key, message, ex) ;
}
}
private void scheduleRetry ( String topic, String key, String message, int retryCount, long delay) {
Timer timer = new Timer ( ) ;
timer. schedule ( new TimerTask ( ) {
@Override
public void run ( ) {
try {
kafkaTemplate. send ( topic, key, message) ;
log. info ( "重试消息发送成功: topic={}, key={}, retryCount={}" , topic, key, retryCount) ;
} catch ( Exception e) {
log. error ( "重试消息发送失败: topic={}, key={}, retryCount={}" , topic, key, retryCount, e) ;
handleFailedMessage ( topic, key, message, e, retryCount) ;
}
}
} , delay) ;
}
private void sendToDeadLetterQueue ( String topic, String key, String message, Exception ex) {
String deadLetterTopic = "dead.letter." + topic;
String deadLetterMessage = buildDeadLetterMessage ( topic, key, message, ex) ;
kafkaTemplate. send ( deadLetterTopic, key, deadLetterMessage) ;
log. info ( "消息已发送到死信队列: topic={}, key={}" , deadLetterTopic, key) ;
}
private String buildDeadLetterMessage ( String originalTopic, String key, String message, Exception ex) {
DeadLetterMessage deadLetterMessage = new DeadLetterMessage ( ) ;
deadLetterMessage. setOriginalTopic ( originalTopic) ;
deadLetterMessage. setKey ( key) ;
deadLetterMessage. setOriginalMessage ( message) ;
deadLetterMessage. setErrorMessage ( ex. getMessage ( ) ) ;
deadLetterMessage. setTimestamp ( System . currentTimeMillis ( ) ) ;
return JsonUtils . toJson ( deadLetterMessage) ;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DeadLetterMessage {
private String originalTopic;
private String key;
private String originalMessage;
private String errorMessage;
private long timestamp;
}
7. 监控和健康检查
@Component
public class KafkaHealthIndicator implements HealthIndicator {
@Autowired
private KafkaTemplate < String , String > kafkaTemplate;
@Override
public Health health ( ) {
try {
kafkaTemplate. send ( "health.check" , "test" , "health check" )
. get ( 5 , TimeUnit . SECONDS ) ;
return Health . up ( )
. withDetail ( "message" , "Kafka连接正常" )
. withDetail ( "timestamp" , System . currentTimeMillis ( ) )
. build ( ) ;
} catch ( Exception e) {
return Health . down ( )
. withDetail ( "error" , e. getMessage ( ) )
. withDetail ( "timestamp" , System . currentTimeMillis ( ) )
. build ( ) ;
}
}
}
@Configuration
public class KafkaMonitoringConfig {
@Bean
public MeterRegistry meterRegistry ( ) {
return new SimpleMeterRegistry ( ) ;
}
@Bean
public KafkaTemplate < String , String > kafkaTemplate ( ProducerFactory < String , String > producerFactory,
MeterRegistry meterRegistry) {
KafkaTemplate < String , String > template = new KafkaTemplate < > ( producerFactory) ;
template. setObservationEnabled ( true ) ;
return template;
}
}
总结
关键要点
核心要点 :
1. 理解Kafka架构 : 掌握Broker、Topic、Partition、Consumer Group的关系
2. 选择合适的配置 : 根据业务需求配置生产者、消费者参数
3. 实现消息可靠性 : 生产者确认、消费者确认、副本配置
4. 处理异常情况 : 重试机制、死信队列、异常处理
5. 性能优化 : 分区策略、批量处理、异步处理
6. 监控和维护 : 健康检查、性能监控、告警机制
最佳实践
实践建议 :
开发阶段 :
- 设计合理的Topic和分区策略
- 选择合适的序列化方式
- 实现消息幂等性
测试阶段 :
- 进行压力测试
- 测试异常场景
- 验证消息可靠性
生产阶段 :
- 配置监控告警
- 定期性能分析
- 及时处理异常
学习路径
学习顺序 :
1. 基础概念 : 理解消息队列和流处理基本概念
2. 架构原理 : 掌握Kafka架构设计
3. 配置参数 : 学习各种配置参数的作用
4. 消息可靠性 : 实现消息不丢失
5. 异常处理 : 处理各种异常情况
6. 性能优化 : 提高系统性能
7. 实战应用 : 在实际项目中应用
Kafka是一个功能强大且高性能的分布式流处理平台,掌握其原理和最佳实践需要持续学习和实践。建议在实际项目中不断应用这些知识,积累经验。