消息队列Kafka与RabbitMQ深度解析:把分布式消息核心讲透,吊打面试官

0 阅读10分钟

消息队列Kafka与RabbitMQ深度解析:把分布式消息核心讲透,吊打面试官

🎯 写在前面:在分布式系统中,消息队列是解耦、削峰、异步通信的核心组件。Kafka和RabbitMQ是最主流的两大消息队列框架。但你真的了解它们的底层原理吗?这篇文章,将带你深度剖析Kafka与RabbitMQ!

一、核心概念:消息队列基础

1.1 为什么需要消息队列?

┌─────────────────────────────────────────────────────────────────────┐
│                    消息队列核心价值                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ 1. 解耦                                                         │ │
│  │                                                                 │ │
│  │   系统A ──────────────────────→ 系统B                          │ │
│  │   系统A ──────── MQ ────────────→ 系统B、C、D                  │ │
│  │                           系统A无需知道谁在消费                   │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ 2. 异步                                                         │ │
│  │                                                                 │ │
│  │   同步:用户下单 → 10ms → 库存扣减 → 50ms → 支付 → 100ms      │ │
│  │   异步:用户下单 → MQ → 返回成功                               │ │
│  │         后台:库存扣减(异步) + 支付(异步) + 发货通知(异步)     │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ 3. 削峰                                                         │ │
│  │                                                                 │ │
│  │   突发流量:10000 QPS → 写入MQ → 后端:100 QPS平稳消费         │ │
│  │   保护系统不被冲垮                                               │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ 4. 广播                                                         │ │
│  │                                                                 │ │
│  │   一次发布,多个消费者订阅                                       │ │
│  │   例:订单创建 → 短信通知 + 邮件通知 + 物流更新 + 数据分析     │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

1.2 Kafka vs RabbitMQ对比

┌─────────────────────────────────────────────────────────────────────┐
│                    Kafka vs RabbitMQ 对比                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌──────────────────────┬──────────────────────────────────────────┐│
│  │        Kafka         │              RabbitMQ                    ││
│  ├──────────────────────┼──────────────────────────────────────────┤│
│  │ 架构:日志追加       │ 架构:队列 + 交换机                      ││
│  │ 存储:分布式日志     │ 存储:内存 + 持久化                      ││
│  │ 消费:拉取(Pull)     │ 消费:推送(Push)                         ││
│  │ 顺序:分区有序       │ 顺序:单队列有序                         ││
│  │ 事务:支持          │ 事务:支持                               ││
│  │ 延迟消息:不支持     │ 延迟消息:支持(插件)                    ││
│  │ 优先级队列:不支持   │ 优先级队列:支持                         ││
│  │ 消息量级:万亿级     │ 消息量级:千万级                         ││
│  │ 吞吐量:百万级QPS   │ 吞吐量:万级QPS                          ││
│  │ 消息回溯:不支持     │ 消息回溯:支持                           ││
│  │ 适用场景:日志、大数据│ 适用场景:业务消息、RPC                  ││
│  └──────────────────────┴──────────────────────────────────────────┘│
│                                                                      │
│  选型建议:                                                          │
│  ✅ 日志采集、大数据流处理 → Kafka                                   │
│  ✅ 业务消息、延迟任务、复杂路由 → RabbitMQ                         │
│  ✅ 追求高性能、大数据量 → Kafka                                    │
│  ✅ 追求可靠性、事务支持 → RabbitMQ                                 │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

二、Kafka深度剖析

2.1 核心架构

┌─────────────────────────────────────────────────────────────────────┐
│                      Kafka核心架构                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │                         ZooKeeper/KRaft                         │ │
│  │                    元数据管理、分区分配、Leader选举              │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                              ↓                                        │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │                      Kafka Cluster                              │ │
│  │                                                                 │ │
│  │   ┌──────────────────────────────────────────────────────────┐  │ │
│  │   │                    Topic: order                          │  │ │
│  │   │                                                           │  │ │
│  │   │  ┌─────────┐    ┌─────────┐    ┌─────────┐              │  │ │
│  │   │  │Partition│    │Partition│    │Partition│              │  │ │
│  │   │  │    0    │    │    1    │    │    2    │              │  │ │
│  │   │  │         │    │         │    │         │              │  │ │
│  │   │  │ P0(R)   │    │ P1(L)   │    │ P2(R)   │              │  │ │
│  │   │  │  Node1  │    │  Node2  │    │  Node3  │              │  │ │
│  │   │  │         │    │         │    │         │              │  │ │
│  │   │  │ P0(L)   │    │ P1(R)   │    │ P2(R)   │              │  │ │
│  │   │  │  Node2  │    │  Node3  │    │  Node1  │              │  │ │
│  │   │  └─────────┘    └─────────┘    └─────────┘              │  │ │
│  │   │         ↑             ↑             ↑                    │  │ │
│  │   │         └─────────────┴─────────────┘                    │  │ │
│  │   │                    Consumer Group                        │  │ │
│  │   └──────────────────────────────────────────────────────────┘  │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
│  L = Leader(主副本)  R = Follower(从副本)                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

2.2 消息存储机制

/**
 * Kafka消息存储结构
 */

// 1. Topic → Partition → Segment
/**
 * Segment = .log文件 + .index文件 + .timeindex文件
 * 
 * 消息存储路径:/data/kafka/topics/order-0/000000000.log
 */

// 2. 消息格式
public class KafkaMessage {
    // Kafka存储的消息结构
    long offset;           // 消息偏移量(全局唯一)
    int messageSize;       // 消息大小
    long timestamp;        // 时间戳
    bytes key;             // 消息键(用于分区)
    bytes value;           // 消息内容
    Headers headers;       // 消息头
    int partition;         // 分区号
    int crc;               // 校验码
}

// 3. 索引机制
/**
 * .index:偏移量索引(快速定位消息)
 * .timeindex:时间戳索引(按时间范围查询)
 * 
 * 稀疏索引:不是每条消息都建索引,默认每间隔4096字节建一条索引
 */

// 4. 日志清理策略
public class LogCleanupPolicy {
    // delete(默认):删除超过保留期的消息
    // compact:只保留每个key的最新消息(用于状态存储)
}

2.3 生产者核心代码

// Kafka生产者配置与代码
public class KafkaProducerDemo {
    
    public static void main(String[] args) {
        // 1. 配置生产者
        Properties props = new Properties();
        props.put("bootstrap.servers", "localhost:9092");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        
        // 可靠性配置
        props.put("acks", "all");           // 所有副本确认
        props.put("retries", 3);            // 重试次数
        props.put("enable.idempotence", true);  // 幂等性
        props.put("max.in.flight.requests.per.connection", 5);
        
        // 性能配置
        props.put("batch.size", 16384);     // 批量大小(16KB)
        props.put("linger.ms", 10);         // 等待时间
        props.put("buffer.memory", 33554432); // 32MB
        
        // 2. 创建生产者
        KafkaProducer<String, String> producer = new KafkaProducer<>(props);
        
        try {
            // 3. 发送消息 - 异步发送
            ProducerRecord<String, String> record = 
                new ProducerRecord<>("order-topic", "order-123", "{\"amount\":100}");
            
            producer.send(record, (metadata, exception) -> {
                if (exception != null) {
                    System.err.println("发送失败:" + exception.getMessage());
                } else {
                    System.out.println("发送成功:" + 
                        "partition=" + metadata.partition() + 
                        ", offset=" + metadata.offset());
                }
            });
            
            // 4. 同步发送
            ProducerRecord<String, String> record2 = 
                new ProducerRecord<>("order-topic", "order-456", "{\"amount\":200}");
            RecordMetadata metadata = producer.send(record2).get();
            System.out.println("同步发送成功:" + metadata.offset());
            
        } finally {
            producer.close();
        }
    }
}

2.4 消费者核心代码

// Kafka消费者配置与代码
public class KafkaConsumerDemo {
    
    public static void main(String[] args) {
        // 1. 配置消费者
        Properties props = new Properties();
        props.put("bootstrap.servers", "localhost:9092");
        props.put("group.id", "order-consumer-group");  // 消费者组
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        
        // 消费配置
        props.put("auto.offset.reset", "earliest");  // earliest/latest
        props.put("enable.auto.commit", false);      // 手动提交偏移量
        props.put("max.poll.records", 500);          // 每次拉取消息数
        props.put("session.timeout.ms", 30000);
        
        // 2. 创建消费者
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        
        // 3. 订阅主题
        consumer.subscribe(Arrays.asList("order-topic", "payment-topic"));
        // 或者使用正则订阅
        // consumer.subscribe(Pattern.compile("order.*"));
        
        try {
            // 4. 拉取消息
            while (true) {
                ConsumerRecords<String, String> records = 
                    consumer.poll(Duration.ofMillis(100));
                
                for (ConsumerRecord<String, String> record : records) {
                    System.out.println("消费消息:" +
                        "topic=" + record.topic() +
                        ", partition=" + record.partition() +
                        ", offset=" + record.offset() +
                        ", key=" + record.key() +
                        ", value=" + record.value());
                    
                    // 业务处理
                    processMessage(record.value());
                }
                
                // 5. 手动提交偏移量
                consumer.commitSync();
            }
        } finally {
            consumer.close();
        }
    }
    
    private static void processMessage(String message) {
        // 业务逻辑处理
    }
}

三、RabbitMQ深度剖析

3.1 核心架构

┌─────────────────────────────────────────────────────────────────────┐
│                      RabbitMQ核心架构                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│                           Producer                                   │
│                              ↓                                        │
│                      ┌─────────────┐                                 │
│                      │   Exchange  │  ← 交换机(路由规则)            │
│                      │  (Type/RoutingKey)│                          │
│                      └──────┬──────┘                                 │
│                             ↓                                         │
│            ┌────────────────┼────────────────┐                     │
│            ↓                ↓                ↓                      │
│      ┌──────────┐    ┌──────────┐    ┌──────────┐                    │
│      │  Queue1  │    │  Queue2  │    │  Queue3  │                    │
│      │  (FIFO)  │    │  (FIFO)  │    │  (FIFO)  │                    │
│      └────┬─────┘    └────┬─────┘    └────┬─────┘                    │
│           ↓                ↓                ↓                       │
│           └────────────────┴────────────────┘                       │
│                              ↓                                        │
│                          Consumer                                     │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │                    Exchange类型                                   │ │
│  │                                                                 │ │
│  │  direct:完全匹配RoutingKey                                       │ │
│  │  fanout:广播给所有绑定的队列                                      │ │
│  │  topic:通配符匹配(*匹配一个词,#匹配零个或多个)                │ │
│  │  headers:匹配消息头的键值对                                       │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

3.2 交换机与队列配置

// RabbitMQ配置类
@Configuration
public class RabbitMQConfig {
    
    public static final String EXCHANGE_NAME = "order.exchange";
    public static final String QUEUE_NAME = "order.queue";
    public static final String ROUTING_KEY = "order.created";
    
    // 1. 创建交换机
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(EXCHANGE_NAME, true, false);
    }
    
    // 2. 创建队列
    @Bean
    public Queue orderQueue() {
        return QueueBuilder.durable(QUEUE_NAME)
            .withArgument("x-dead-letter-exchange", "dlx.exchange")  // 死信交换机
            .withArgument("x-dead-letter-routing-key", "dlx.order")
            .build();
    }
    
    // 3. 绑定交换机和队列
    @Bean
    public Binding orderBinding() {
        return BindingBuilder
            .bind(orderQueue())
            .to(orderExchange())
            .with(ROUTING_KEY);
    }
}

// 发送消息
@Service
public class OrderServiceImpl {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendOrder(Order order) {
        // 方式1:发送消息
        rabbitTemplate.convertAndSend(
            RabbitMQConfig.EXCHANGE_NAME,
            RabbitMQConfig.ROUTING_KEY,
            order
        );
        
        // 方式2:发送消息并指定消息属性
        MessageProperties props = new MessageProperties();
        props.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        props.setExpiration("30000");  // 30秒TTL
        props.setPriority(5);  // 优先级
        Message message = new Message(order.toString().getBytes(), props);
        rabbitTemplate.send(message);
    }
}

// 消费消息
@Component
public class OrderConsumer {
    
    @RabbitListener(queues = RabbitMQConfig.QUEUE_NAME)
    public void handleOrder(Order order, Message message, Channel channel) 
            throws IOException {
        
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        
        try {
            // 业务处理
            processOrder(order);
            
            // 确认消息
            channel.basicAck(deliveryTag, false);
            
        } catch (Exception e) {
            // 处理失败,重试或拒绝
            if (isRetryable(e)) {
                // 重试
                channel.basicNack(deliveryTag, false, true);
            } else {
                // 进入死信队列
                channel.basicNack(deliveryTag, false, false);
            }
        }
    }
}

3.3 延迟消息实现

// 方式1:TTL + 死信队列(简单延迟)
@Configuration
public class DelayQueueConfig {
    
    public static final String DELAY_EXCHANGE = "delay.exchange";
    public static final String DELAY_QUEUE = "delay.queue";
    public static final String NORMAL_QUEUE = "normal.queue";
    
    @Bean
    public CustomExchange delayExchange() {
        // x-delayed-message 需要rabbitmq_delayed_message_exchange插件
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAY_EXCHANGE, "x-delayed-message", true, false, args);
    }
    
    @Bean
    public Queue delayQueue() {
        return QueueBuilder.durable(DELAY_QUEUE).build();
    }
    
    @Bean
    public Binding delayBinding() {
        return BindingBuilder
            .bind(delayQueue())
            .to(delayExchange())
            .with("delay.key")
            .noargs();
    }
}

// 发送延迟消息(延迟10秒)
@Service
public class DelayMessageService {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendDelayMessage(Order order, long delayMillis) {
        rabbitTemplate.convertAndSend(
            DelayQueueConfig.DELAY_EXCHANGE,
            "delay.key",
            order,
            message -> {
                message.getMessageProperties().setDelay((int) delayMillis);
                return message;
            }
        );
    }
}

四、消息可靠性:核心挑战

4.1 消息可靠性保障机制

┌─────────────────────────────────────────────────────────────────────┐
│                    消息可靠性保障体系                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  生产者 ─────────────→ Broker ─────────────→ 消费者                 │
│                                                                      │
│  ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐   │
│  │   生产者确认     │   │   消息持久化     │   │   手动确认      │   │
│  │                 │   │                 │   │                 │   │
│  │ publisher       │   │ synchronous     │   │ basic.ack      │   │
│  │ confirms        │   │ flush           │   │                │   │
│  │                 │   │                 │   │ auto ack vs    │   │
│  │ Transaction     │   │ Page Cache      │   │ manual ack     │   │
│  └─────────────────┘   └─────────────────┘   └─────────────────┘   │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │                    可靠性级别                                    │ │
│  │                                                                 │ │
│  │  at-most-once:最多一次(可能丢消息)                           │ │
│  │  at-least-once:至少一次(可能重复消费)← 业务幂等性必须         │ │
│  │  exactly-once:精确一次(代价高昂)                             │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

4.2 Kafka可靠性配置

// Kafka可靠性配置
public class KafkaReliabilityConfig {
    
    public static Properties getReliableConfig() {
        Properties props = new Properties();
        
        // 1. 生产者可靠性
        props.put("acks", "all");              // 所有ISR副本确认
        props.put("retries", 3);                // 重试次数
        props.put("enable.idempotence", true);  // 幂等性(防止重复发送)
        
        // 2. 消息持久化
        props.put("batch.size", 16384);         // 批量大小
        props.put("linger.ms", 5);              // 批次等待时间
        props.put("buffer.memory", 33554432);   // 32MB缓冲区
        
        // 3. 消费者可靠性
        props.put("auto.offset.reset", "earliest");  // earliest/latest
        props.put("enable.auto.commit", false);      // 手动提交偏移量
        
        // 4. Broker配置
        // replication.factor >= 3
        // min.insync.replicas = 2
        
        return props;
    }
}

// 消费者手动提交偏移量
public class ReliableConsumer {
    
    public void consumeReliable() {
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("order-topic"));
        
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
            
            // 批量处理
            List<Order> orders = new ArrayList<>();
            for (ConsumerRecord<String, String> record : records) {
                Order order = parseOrder(record.value());
                orders.add(order);
            }
            
            // 业务处理
            if (processOrders(orders)) {
                // 业务处理成功,提交偏移量
                consumer.commitSync();
            } else {
                // 业务处理失败,不提交偏移量,等待重试
            }
        }
    }
}

4.3 幂等性处理

// 消息幂等性处理方案
@Service
public class IdempotentService {
    
    // 方案1:数据库唯一索引
    @Autowired
    private OrderMapper orderMapper;
    
    public void processOrderWithUniqueIndex(Order order) {
        try {
            // 使用唯一索引防止重复插入
            orderMapper.insert(order);
        } catch (DuplicateKeyException e) {
            // 重复消息,忽略
            log.warn("重复消息: {}", order.getOrderId());
        }
    }
    
    // 方案2:Redis缓存去重
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public void processOrderWithRedis(Order order) {
        String key = "order:processed:" + order.getOrderId();
        Boolean success = redisTemplate.opsForValue()
            .setIfAbsent(key, "1", Duration.ofHours(24));
        
        if (Boolean.TRUE.equals(success)) {
            // 首次处理
            doProcess(order);
        } else {
            // 重复消息
            log.warn("重复消息: {}", order.getOrderId());
        }
    }
    
    // 方案3:状态机幂等
    @Transactional
    public void processOrderWithStateMachine(Order order) {
        // 只有PENDING状态才能转为PROCESSING
        int rows = orderMapper.updateStatusIfCurrent(
            order.getOrderId(),
            OrderStatus.PENDING,
            OrderStatus.PROCESSING
        );
        
        if (rows > 0) {
            // 首次处理成功
            doProcess(order);
        } else {
            // 状态已变更,可能是重复消息
            log.warn("状态已变更,忽略: {}", order.getOrderId());
        }
    }
}

五、消息顺序保障

5.1 消息顺序问题

┌─────────────────────────────────────────────────────────────────────┐
│                    消息顺序问题                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Kafka顺序保证:                                                      │
│  - 单分区有序:一个Partition内的消息有序                              │
│  - 全局无序:不同Partition之间无法保证顺序                            │
│                                                                      │
│  问题场景:                                                          │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │  消息1:创建订单                                                │ │
│  │  消息2:支付订单(依赖订单创建)                                 │ │
│  │  消息3:取消订单                                                │ │
│  │                                                                 │ │
│  │  问题:如果消费顺序是 213,支付在创建之前,会导致错误!    │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

5.2 保证顺序的解决方案

// 方案1:使用单个Partition(Kafka)
public class SinglePartitionProducer {
    
    public void sendOrderedMessages(List<OrderMessage> messages) {
        KafkaProducer<String, String> producer = new KafkaProducer<>(props);
        
        for (OrderMessage msg : messages) {
            // 使用相同key保证发送到同一Partition
            ProducerRecord<String, String> record = 
                new ProducerRecord<>(
                    "order-topic",
                    msg.getOrderId(),  // 相同orderId → 相同Partition
                    msg.toString()
                );
            producer.send(record);
        }
    }
}

// 方案2:顺序消息队列(RabbitMQ)
/**
 * RabbitMQ单队列天然保证FIFO顺序
 * 只需要:
 * 1. 一个消费者串行处理
 * 2. 或者使用独占队列
 */

// 方案3:多阶段顺序处理
public class MultiStageOrderProcessor {
    
    // 创建订单阶段
    @RabbitListener(queues = "order.create.queue")
    public void processCreate(Order order) {
        orderMapper.create(order);
        // 发送支付消息
        rabbitTemplate.convertAndSend("order.pay.exchange", "pay", order);
    }
    
    // 支付阶段
    @RabbitListener(queues = "order.pay.queue")
    public void processPay(Order order) {
        // 先检查订单状态
        Order current = orderMapper.findById(order.getId());
        if (current.getStatus() != OrderStatus.CREATED) {
            log.warn("订单状态异常,跳过支付:{}", order.getId());
            return;
        }
        orderMapper.pay(order);
    }
}

六、常见面试题

Q1:如何保证消息不丢失?

┌─────────────────────────────────────────────────────────────────────┐
│                    消息不丢失保障措施                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  生产者侧:                                                           │
│  ✅ acks=all(所有副本确认)                                         │
│  ✅ enable.idempotence=true(幂等发送)                              │
│  ✅ retries=3(失败重试)                                            │
│  ✅ callback确认发送结果                                              │
│                                                                      │
│  Broker侧:                                                          │
│  ✅ replication.factor >= 3                                         │
│  ✅ min.insync.replicas >= 2                                        │
│  ✅ flush到磁盘(不过度影响性能)                                     │
│                                                                      │
│  消费者侧:                                                          │
│  ✅ enable.auto.commit=false(手动提交偏移量)                       │
│  ✅ 业务处理成功后再提交                                              │
│  ✅ 幂等性处理(防止重复消费)                                       │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Q2:如何处理消息堆积?

问题:消费者处理速度慢,导致消息堆积

解决方案:

1. 扩容消费者
   - 增加消费者实例(同一Consumer Group)
   - 前提: Partition数量足够

2. 消费者优化
   - 批量处理代替逐条处理
   - 异步处理 + 批量确认
   - 优化业务逻辑(减少数据库IO)

3. 临时方案
   - Kafka:创建新消费者从尾巴消费(跳过堆积)
   - RabbitMQ:惰性队列(存磁盘)

4. 监控预警
   - 监控消费延迟
   - 设置堆积阈值告警

Q3:Kafka如何实现Exactly-Once?

Kafka Exactly-Once语义实现:

┌─────────────────────────────────────────────────────────────────────┐
│                    Kafka事务机制                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. 幂等性Producer(单会话内幂等)                                    │
│     enable.idempotence=true                                          │
│     Producer有唯一PID,消息有唯一序列号                               │
│     Broker去重                                                        │
│                                                                      │
│  2. 事务API(跨会话/跨分区)                                         │
│     producer.initTransactions();                                     │
│     producer.beginTransaction();                                      │
│     producer.send(record);                                            │
│     producer.sendOffsetsToTransaction(consumerOffsets, consumerGroupId); │
│     producer.commitTransaction();                                     │
│                                                                      │
│  3. Exactly-Once Source Connector                                   │
│     Kafka Connect支持exactly-once读取外部系统                         │
│                                                                      │
│  注意:消费端的exactly-once需要业务配合                               │
│  - 使用事务保证消费和偏移量提交的原子性                               │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

七、总结

┌─────────────────────────────────────────────────────────────────────┐
│                      消息队列知识地图                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Kafka ────────────────────────────────────────────────────────→   │
│  Topic/Partition/Segment → ISR/Leader选举 → 幂等性/事务             │
│                                                                      │
│  RabbitMQ ──────────────────────────────────────────────────────→   │
│  Exchange/Queue/Binding → 交换机类型 → 延迟消息                     │
│                                                                      │
│  可靠性 ────────────────────────────────────────────────────────→   │
│  生产者确认 → 持久化 → 消费者确认 → 幂等性                           │
│                                                                      │
│  顺序保证 ──────────────────────────────────────────────────────→   │
│  单Partition → 全局顺序                                               │
│                                                                      │
│  选型指南 ──────────────────────────────────────────────────────→   │
│  日志/大数据 → Kafka                                                │
│  业务消息/RPC → RabbitMQ                                            │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

🎯 讨论话题

大家在使用消息队列时遇到过哪些"坑"?是怎么解决的?


往期热门文章推荐:


**如果这篇文章对你有帮助,欢迎点赞、收藏、转发!我们下期再见!**👋